diff --git a/.buildkite/pipeline-resource-definitions/README.md b/.buildkite/pipeline-resource-definitions/README.md new file mode 100644 index 0000000000000..07a3e54fd93bd --- /dev/null +++ b/.buildkite/pipeline-resource-definitions/README.md @@ -0,0 +1,28 @@ +# Buildkite pipeline resource definitions + +## Overview +The pipeline resources are "RRE" (real resource entities) that are used to create/maintain buildkite pipelines. + +The resources described in these files are parsed and loaded to Backstage (https://backstage.elastic.dev). +From there, [Terrazzo](https://buildkite.com/elastic/terrazzo/) is generating and updating the buildkite pipelines. + +These pipelines are referenced indirectly through the root's [`catalog-info.yaml`](../../catalog-info.yaml) file in order to reduce bloat in the main resources file. +There's a location file that collects files defined in this folder ([locations.yml](locations.yml)), this file needs to be updated in order to keep track of local files. + +Available parameters and further help can be found here: https://docs.elastic.dev/ci/getting-started-with-buildkite-at-elastic + +## Creating a new pipeline resource definition +The easiest way to create a new pipeline is either by copying and editing a similar pipeline, +or by copying a blank template (see [_new_pipeline.yml](_templates/_new_pipeline.yml)) and editing that. + +You can validate your pipeline's structural integrity, and it's conformity to baseline rules by running the following command: +```bash +.buildkite/pipeline-resource-definitions/scripts/validate-pipeline-definition.sh +``` + +Once you've added the file, you should update the [locations.yml](locations.yml) file to include the new pipeline, or run the following command to update it: +```bash +.buildkite/pipeline-resource-definitions/scripts/fix-location-collection.ts +``` + +Add your pipeline implementation, commit & push & merge. The pipeline resource will appear in Backstage within minutes, then the pipeline will be added to Buildkite within ~10 minutes. \ No newline at end of file diff --git a/.buildkite/pipeline-resource-definitions/_template/template.yml b/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml similarity index 100% rename from .buildkite/pipeline-resource-definitions/_template/template.yml rename to .buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml index 46c31b20ec009..e1c40f690f4ec 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml @@ -22,7 +22,6 @@ spec: ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' allow_rebuilds: true - branch_configuration: main 8.15 8.14 7.17 default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/artifacts.yml @@ -44,20 +43,3 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ - schedules: - Daily build (main): - cronline: 0 7 * * * America/New_York - message: Daily build - branch: main - Daily build (8.15): - cronline: 0 7 * * * America/New_York - message: Daily build - branch: '8.15' - Daily build (8.14): - cronline: 0 7 * * * America/New_York - message: Daily build - branch: '8.14' - Daily build (7.17): - cronline: 0 7 * * * America/New_York - message: Daily build - branch: '7.17' diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml index c3cb560290280..71bcc4079c50d 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml @@ -23,7 +23,6 @@ spec: ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' allow_rebuilds: true - branch_configuration: 7.17 8.14 8.15 repository: elastic/kibana pipeline_file: .buildkite/pipelines/artifacts.yml skip_intermediate_builds: false @@ -44,16 +43,3 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ - schedules: - Daily build (8.15): - cronline: 0 7 * * * America/New_York - message: Daily build - branch: '8.15' - Daily build (8.14): - cronline: 0 7 * * * America/New_York - message: Daily build - branch: '8.14' - Daily build (7.17): - cronline: 0 7 * * * America/New_York - message: Daily build - branch: '7.17' diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml index 7d83453dab783..8a9585762de83 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml @@ -23,7 +23,6 @@ spec: ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' allow_rebuilds: true - branch_configuration: 8.15 8.14 default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/artifacts_trigger.yml @@ -45,12 +44,3 @@ spec: access_level: MANAGE_BUILD_AND_READ kibana-tech-leads: access_level: MANAGE_BUILD_AND_READ - schedules: - Daily build (8.15): - cronline: 0 */2 * * * America/New_York - message: Daily build - branch: '8.15' - Daily build (8.14): - cronline: 0 */2 * * * America/New_York - message: Daily build - branch: '8.14' diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml b/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml new file mode 100644 index 0000000000000..dea4426e60e1b --- /dev/null +++ b/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml @@ -0,0 +1,41 @@ +# yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: bk-kibana-es-forward-compatibility-testing + description: Forward compatibility testing between Kibana 7.17 and ES 8+ + links: + - url: 'https://buildkite.com/elastic/kibana-es-forward-compatibility-testing' + title: Pipeline link +spec: + type: buildkite-pipeline + system: buildkite + owner: 'group:kibana-operations' + implementation: + apiVersion: buildkite.elastic.dev/v1 + kind: Pipeline + metadata: + name: kibana / ES Forward Compatibility Testing + description: Forward compatibility testing between Kibana 7.17 and ES 8+ + spec: + env: + SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' + ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' + allow_rebuilds: false + branch_configuration: main + default_branch: main + repository: elastic/kibana + pipeline_file: .buildkite/pipelines/es_forward.yml # Note: this file exists in 7.17 only + skip_intermediate_builds: false + provider_settings: + prefix_pull_request_fork_branch_names: false + trigger_mode: none + teams: + kibana-operations: + access_level: MANAGE_BUILD_AND_READ + appex-qa: + access_level: MANAGE_BUILD_AND_READ + kibana-tech-leads: + access_level: MANAGE_BUILD_AND_READ + everyone: + access_level: BUILD_AND_READ diff --git a/.buildkite/pipeline-resource-definitions/locations.yml b/.buildkite/pipeline-resource-definitions/locations.yml index c4e07b0d055c1..35115b00e273b 100644 --- a/.buildkite/pipeline-resource-definitions/locations.yml +++ b/.buildkite/pipeline-resource-definitions/locations.yml @@ -14,6 +14,7 @@ spec: - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml + - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml @@ -41,3 +42,4 @@ spec: - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml + - https://github.com/elastic/kibana/blob/main/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml diff --git a/.buildkite/pipeline-resource-definitions/fix-location-collection.ts b/.buildkite/pipeline-resource-definitions/scripts/fix-location-collection.ts similarity index 83% rename from .buildkite/pipeline-resource-definitions/fix-location-collection.ts rename to .buildkite/pipeline-resource-definitions/scripts/fix-location-collection.ts index d4e36f2559a89..1173cddeb15aa 100755 --- a/.buildkite/pipeline-resource-definitions/fix-location-collection.ts +++ b/.buildkite/pipeline-resource-definitions/scripts/fix-location-collection.ts @@ -11,7 +11,7 @@ import jsYaml from 'js-yaml'; import path from 'path'; import { execSync } from 'child_process'; -const EXCLUDE_LIST = ['locations.yml', '_template/template.yml']; +const EXCLUDE_LIST = ['locations.yml', '_templates']; const REPO_FILES_BASE = 'https://github.com/elastic/kibana/blob/main'; type BackstageLocationResource = object & { @@ -20,19 +20,19 @@ type BackstageLocationResource = object & { async function main() { const repoRoot = execSync('git rev-parse --show-toplevel').toString().trim(); - const resourceDefinitionsFolder = path.resolve( + const resourceDefinitionsRoot = path.resolve( repoRoot, '.buildkite', 'pipeline-resource-definitions' ); const resourceDefinitionsBaseUrl = `${REPO_FILES_BASE}/.buildkite/pipeline-resource-definitions`; - const locationFile = path.resolve(resourceDefinitionsFolder, 'locations.yml'); + const locationFile = path.resolve(resourceDefinitionsRoot, 'locations.yml'); const locationFileLines = fs.readFileSync(locationFile, 'utf8').split('\n'); - const pipelines = readDirRecursively(resourceDefinitionsFolder) + const pipelines = readDirRecursively(resourceDefinitionsRoot) .filter((file) => file.endsWith('.yml')) - .map((file) => file.replace(`${resourceDefinitionsFolder}/`, '')) - .filter((f) => !EXCLUDE_LIST.includes(f)); + .map((file) => file.replace(`${resourceDefinitionsRoot}/`, '')) + .filter((f) => EXCLUDE_LIST.every((excludeExpr) => !f.match(excludeExpr))); const preamble = locationFileLines.slice(0, 1); diff --git a/.buildkite/pipeline-resource-definitions/scripts/validate-pipeline-definition.sh b/.buildkite/pipeline-resource-definitions/scripts/validate-pipeline-definition.sh new file mode 100755 index 0000000000000..cd153b23f10ab --- /dev/null +++ b/.buildkite/pipeline-resource-definitions/scripts/validate-pipeline-definition.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# This script is used to validate a single RRE for a pipeline definition. + +TARGET_FILE=$1 + +if [ -z "$TARGET_FILE" ]; then + echo "Usage: $0 " + exit 1 +fi + +echo "Validating $TARGET_FILE..." +ABSOLUTE_PATH=$(realpath "$TARGET_FILE") +FILE_NAME=$(basename "$ABSOLUTE_PATH") +FOLDER_NAME=$(dirname "$ABSOLUTE_PATH") + +docker run -it \ + --mount type=bind,source="$FOLDER_NAME",target=/home/app/ \ + docker.elastic.co/ci-agent-images/pipelib \ + rre validate --backstage-entity-aware "/home/app/$FILE_NAME" + +if [ $? -ne 0 ]; then + echo "$FILE_NAME invalid ❌" + exit 1 +else + echo "$FILE_NAME valid ✅" + exit 0 +fi diff --git a/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml b/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml new file mode 100644 index 0000000000000..ea474356b137d --- /dev/null +++ b/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml @@ -0,0 +1,71 @@ +### +# For more information on authoring pipeline definitions, +# follow the guides at https://docs.elastic.dev/ci/getting-started-with-buildkite-at-elastic +### +# yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: bk-kibana-trigger-version-dependent-jobs + description: 'Trigger version-dependent jobs' + links: + - url: 'https://buildkite.com/elastic/kibana-trigger-version-dependent-jobs' + title: Pipeline link +spec: + type: buildkite-pipeline + system: buildkite + owner: 'group:kibana-operations' + implementation: + apiVersion: buildkite.elastic.dev/v1 + kind: Pipeline + metadata: + name: kibana / trigger version-dependent jobs + description: 'Trigger version-dependent jobs' + spec: + env: + SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' + ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' + + allow_rebuilds: false + branch_configuration: main + default_branch: main + repository: elastic/kibana + pipeline_file: .buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.sh + skip_intermediate_builds: false + provider_settings: + prefix_pull_request_fork_branch_names: false + skip_pull_request_builds_for_existing_commits: true + trigger_mode: none + teams: + kibana-operations: + access_level: MANAGE_BUILD_AND_READ + appex-qa: + access_level: MANAGE_BUILD_AND_READ + kibana-tech-leads: + access_level: MANAGE_BUILD_AND_READ + everyone: + access_level: BUILD_AND_READ + schedules: + Trigger ES forward compatibility tests: + cronline: 0 5 * * * + message: Trigger ES forward compatibility tests + env: + TRIGGER_PIPELINE_SET: es-forward + Trigger artifact staging builds: + cronline: 0 7 * * * America/New_York + message: Trigger artifact staging builds + env: + TRIGGER_PIPELINE_SET: artifacts-staging + MESSAGE: Daily staging build + Trigger artifact snapshot builds: + cronline: 0 7 * * * America/New_York + message: Trigger artifact snapshot builds + env: + TRIGGER_PIPELINE_SET: artifacts-snapshot + MESSAGE: Daily snapshot build + Run kibana-artifacts-trigger: + cronline: 0 */2 * * * America/New_York + message: Trigger 'kibana-artifacts-trigger' + env: + TRIGGER_PIPELINE_SET: artifacts-trigger + MESSAGE: Daily build diff --git a/.buildkite/pipeline-utils/agent_images.ts b/.buildkite/pipeline-utils/agent_images.ts index 0606f036b1c64..d139f7953e00f 100644 --- a/.buildkite/pipeline-utils/agent_images.ts +++ b/.buildkite/pipeline-utils/agent_images.ts @@ -52,4 +52,19 @@ function getAgentImageConfig({ returnYaml = false } = {}): string | AgentImageCo return config; } -export { getAgentImageConfig }; +const expandAgentQueue = (queueName: string = 'n2-4-spot') => { + const [kind, cores, addition] = queueName.split('-'); + const additionalProps = + { + spot: { preemptible: true }, + virt: { localSsdInterface: 'nvme', enableNestedVirtualization: true, localSsds: 1 }, + }[addition] || {}; + + return { + ...getAgentImageConfig(), + machineType: `${kind}-standard-${cores}`, + ...additionalProps, + }; +}; + +export { getAgentImageConfig, expandAgentQueue }; diff --git a/.buildkite/pipeline-utils/ci-stats/pick_test_group_run_order.ts b/.buildkite/pipeline-utils/ci-stats/pick_test_group_run_order.ts index 20b2d366e6067..a24214b1e62b0 100644 --- a/.buildkite/pipeline-utils/ci-stats/pick_test_group_run_order.ts +++ b/.buildkite/pipeline-utils/ci-stats/pick_test_group_run_order.ts @@ -16,27 +16,10 @@ import { BuildkiteClient, BuildkiteStep } from '../buildkite'; import { CiStatsClient, TestGroupRunOrderResponse } from './client'; import DISABLED_JEST_CONFIGS from '../../disabled_jest_configs.json'; -import { getAgentImageConfig } from '#pipeline-utils'; +import { expandAgentQueue } from '#pipeline-utils'; type RunGroup = TestGroupRunOrderResponse['types'][0]; -// TODO: remove this after https://github.com/elastic/kibana-operations/issues/15 is finalized -/** This function bridges the agent targeting between gobld and kibana-buildkite agent targeting */ -const getAgentRule = (queueName: string = 'n2-4-spot') => { - if (process.env?.BUILDKITE_AGENT_META_DATA_QUEUE === 'gobld') { - const [kind, cores, spot] = queueName.split('-'); - return { - ...getAgentImageConfig(), - machineType: `${kind}-standard-${cores}`, - preemptible: spot === 'spot', - }; - } else { - return { - queue: queueName, - }; - } -}; - const getRequiredEnv = (name: string) => { const value = process.env[name]; if (typeof value !== 'string' || !value) { @@ -436,7 +419,7 @@ export async function pickTestGroupRunOrder() { parallelism: unit.count, timeout_in_minutes: 120, key: 'jest', - agents: getAgentRule('n2-4-spot'), + agents: expandAgentQueue('n2-4-spot'), retry: { automatic: [ { exit_status: '-1', limit: 3 }, @@ -454,7 +437,7 @@ export async function pickTestGroupRunOrder() { parallelism: integration.count, timeout_in_minutes: 120, key: 'jest-integration', - agents: getAgentRule('n2-4-spot'), + agents: expandAgentQueue('n2-4-spot'), retry: { automatic: [ { exit_status: '-1', limit: 3 }, @@ -488,7 +471,7 @@ export async function pickTestGroupRunOrder() { label: title, command: getRequiredEnv('FTR_CONFIGS_SCRIPT'), timeout_in_minutes: 90, - agents: getAgentRule(queue), + agents: expandAgentQueue(queue), env: { FTR_CONFIG_GROUP_KEY: key, ...FTR_EXTRA_ARGS, diff --git a/.buildkite/pipeline-utils/utils.test.ts b/.buildkite/pipeline-utils/utils.test.ts new file mode 100644 index 0000000000000..2fece6082bc5c --- /dev/null +++ b/.buildkite/pipeline-utils/utils.test.ts @@ -0,0 +1,45 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-unused-expressions */ + +import { expect } from 'chai'; +import { getKibanaDir, getVersionsFile } from './utils'; +import fs from 'fs'; + +// TODO: replace mocha with jest, and write tests that mock FS + +describe('getKibanaDir', () => { + it('should return the kibana directory', () => { + const kibanaDir = getKibanaDir(); + + expect(kibanaDir).to.be.ok; + expect(fs.existsSync(kibanaDir)).to.be.true; + }); +}); + +describe('getVersionsFile', () => { + it('should return the versions file', () => { + const versionsFile = getVersionsFile(); + + expect(versionsFile).to.be.ok; + expect(versionsFile.versions).to.be.an('array'); + }); + + it('should correctly find prevMajor and prevMinor versions', () => { + const versionsFile = getVersionsFile(); + + expect(versionsFile.prevMajors).to.be.an('array'); + expect(versionsFile.prevMajors.length).to.eql(1); + expect(versionsFile.prevMajors[0].branch).to.eql('7.17'); + + expect(versionsFile.prevMinors).to.be.an('array'); + }); + + // TODO: write more tests with mocking... +}); diff --git a/.buildkite/pipeline-utils/utils.ts b/.buildkite/pipeline-utils/utils.ts index e9a5cf9193334..9f7a27d6e1518 100644 --- a/.buildkite/pipeline-utils/utils.ts +++ b/.buildkite/pipeline-utils/utils.ts @@ -7,6 +7,8 @@ */ import { execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; const getKibanaDir = (() => { let kibanaDir: string | undefined; @@ -21,4 +23,42 @@ const getKibanaDir = (() => { }; })(); -export { getKibanaDir }; +export interface Version { + branch: string; + version: string; +} +export interface VersionsFile { + versions: Array< + { + previousMajor?: boolean; + previousMinor?: boolean; + currentMajor?: boolean; + currentMinor?: boolean; + } & Version + >; +} +const getVersionsFile = (() => { + let versions: VersionsFile & { + prevMinors: Version[]; + prevMajors: Version[]; + current: Version; + }; + const versionsFileName = 'versions.json'; + try { + const versionsJSON = JSON.parse( + fs.readFileSync(path.join(getKibanaDir(), versionsFileName)).toString() + ); + versions = { + versions: versionsJSON.versions, + prevMinors: versionsJSON.versions.filter((v: any) => v.previousMinor), + prevMajors: versionsJSON.versions.filter((v: any) => v.previousMajor), + current: versionsJSON.versions.find((v: any) => v.currentMajor && v.currentMinor), + }; + } catch (error) { + throw new Error(`Failed to read ${versionsFileName}: ${error}`); + } + + return () => versions; +})(); + +export { getKibanaDir, getVersionsFile }; diff --git a/.buildkite/pipelines/flaky_tests/pipeline.ts b/.buildkite/pipelines/flaky_tests/pipeline.ts index 6a5b7da38a143..d77504deacb45 100644 --- a/.buildkite/pipelines/flaky_tests/pipeline.ts +++ b/.buildkite/pipelines/flaky_tests/pipeline.ts @@ -7,7 +7,7 @@ */ import { groups } from './groups.json'; -import { BuildkiteStep } from '#pipeline-utils'; +import { BuildkiteStep, expandAgentQueue } from '#pipeline-utils'; const configJson = process.env.KIBANA_FLAKY_TEST_RUNNER_CONFIG; if (!configJson) { @@ -32,34 +32,6 @@ if (Number.isNaN(concurrency)) { const BASE_JOBS = 1; const MAX_JOBS = 500; -// TODO: remove this after https://github.com/elastic/kibana-operations/issues/15 is finalized -/** This function bridges the agent targeting between gobld and kibana-buildkite agent targeting */ -const getAgentRule = (queueName: string = 'n2-4-spot') => { - if ( - process.env.BUILDKITE_AGENT_META_DATA_QUEUE === 'gobld' || - process.env.BUILDKITE_AGENT_META_DATA_PROVIDER === 'k8s' - ) { - const [kind, cores, addition] = queueName.split('-'); - const additionalProps = - { - spot: { preemptible: true }, - virt: { localSsdInterface: 'nvme', enableNestedVirtualization: true, localSsds: 1 }, - }[addition] || {}; - - return { - provider: 'gcp', - image: 'family/kibana-ubuntu-2004', - imageProject: 'elastic-images-prod', - machineType: `${kind}-standard-${cores}`, - ...additionalProps, - }; - } else { - return { - queue: queueName, - }; - } -}; - function getTestSuitesFromJson(json: string) { const fail = (errorMsg: string) => { console.error('+++ Invalid test config provided'); @@ -150,7 +122,7 @@ const pipeline = { steps.push({ command: '.buildkite/scripts/steps/build_kibana.sh', label: 'Build Kibana Distribution and Plugins', - agents: getAgentRule('c2-8'), + agents: expandAgentQueue('c2-8'), key: 'build', if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''", }); @@ -173,7 +145,7 @@ for (const testSuite of testSuites) { concurrency, concurrency_group: process.env.UUID, concurrency_method: 'eager', - agents: getAgentRule('n2-4-spot'), + agents: expandAgentQueue('n2-4-spot'), depends_on: 'build', timeout_in_minutes: 150, cancel_on_build_failing: true, @@ -197,7 +169,7 @@ for (const testSuite of testSuites) { steps.push({ command: `.buildkite/scripts/steps/functional/${suiteName}.sh`, label: group.name, - agents: getAgentRule(agentQueue), + agents: expandAgentQueue(agentQueue), key: `cypress-suite-${suiteIndex++}`, depends_on: 'build', timeout_in_minutes: 150, @@ -233,7 +205,7 @@ pipeline.steps.push({ pipeline.steps.push({ command: 'ts-node .buildkite/pipelines/flaky_tests/post_stats_on_pr.ts', label: 'Post results on Github pull request', - agents: getAgentRule('n2-4-spot'), + agents: expandAgentQueue('n2-4-spot'), timeout_in_minutes: 15, retry: { automatic: [{ exit_status: '-1', limit: 3 }], diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index ae6c05721ae84..4eb15c16970ef 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -16,28 +16,6 @@ steps: limit: 1 - wait - - label: 'Triggering changes-based pipelines' - branches: main - agents: - image: family/kibana-ubuntu-2004 - imageProject: elastic-images-prod - provider: gcp - machineType: n2-standard-2 - # TODO: this can probably be deleted after the migration https://github.com/elastic/kibana-operations/issues/15 - plugins: - - chronotc/monorepo-diff#v2.0.4: - watch: - - path: - - 'versions.json' - config: - command: 'ts-node .buildkite/scripts/steps/trigger_pipeline.ts kibana-buildkite-pipelines-deploy main' - label: 'Trigger pipeline deploy' - agents: - image: family/kibana-ubuntu-2004 - imageProject: elastic-images-prod - provider: gcp - machineType: n2-standard-2 - - command: .buildkite/scripts/steps/on_merge_build_and_metrics.sh label: Build Kibana Distribution and Plugins agents: diff --git a/.buildkite/pull_requests.json b/.buildkite/pull_requests.json index 0c6714d1c75b7..0758e0255247f 100644 --- a/.buildkite/pull_requests.json +++ b/.buildkite/pull_requests.json @@ -45,52 +45,6 @@ "/__snapshots__/", "\\.test\\.(ts|tsx|js|jsx)" ] - }, - { - "repoOwner": "elastic", - "repoName": "kibana", - "pipelineSlug": "kibana-kme-test", - - "enabled": true, - "allow_org_users": true, - "allowed_repo_permissions": ["admin", "write"], - "set_commit_status": true, - "commit_status_context": "kibana-ci-test", - "build_on_commit": true, - "build_on_comment": false, - "trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))", - "skip_ci_labels": [], - "labels": ["kme-test"], - "skip_target_branches": ["6.8", "7.11", "7.12"], - "enable_skippable_commits": true, - "skip_ci_on_only_changed": [ - "^dev_docs/", - "^docs/", - "^rfcs/", - "^\\.github/", - "\\.md$", - "\\.mdx$", - "^api_docs/.+\\.devdocs\\.json$", - "^\\.backportrc\\.json$", - "^nav-kibana-dev\\.docnav\\.json$", - "^src/dev/prs/kibana_qa_pr_list\\.json$", - "^\\.buildkite/pull_requests\\.json$", - "^\\.catalog-info\\.yaml$" - ], - "always_require_ci_on_changed": [ - "^docs/developer/plugin-list.asciidoc$", - "^\\.github/CODEOWNERS$", - "/plugins/[^/]+/readme\\.(md|asciidoc)$" - ], - "kibana_versions_check": true, - "kibana_build_reuse": true, - "kibana_build_reuse_pipeline_slugs": ["kibana-kme-test", "kibana-on-merge"], - "kibana_build_reuse_regexes": [ - "^test/", - "^x-pack/test/", - "/__snapshots__/", - "\\.test\\.(ts|tsx|js|jsx)" - ] } ] } diff --git a/.buildkite/scripts/common/util.sh b/.buildkite/scripts/common/util.sh index 5630fed40bf93..bc5983e249669 100755 --- a/.buildkite/scripts/common/util.sh +++ b/.buildkite/scripts/common/util.sh @@ -172,3 +172,9 @@ npm_install_global() { download_artifact() { retry 3 1 timeout 3m buildkite-agent artifact download "$@" } + +print_if_dry_run() { + if [[ "${DRY_RUN:-}" =~ ^(1|true)$ ]]; then + echo "DRY_RUN is enabled." + fi +} diff --git a/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.sh b/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.sh new file mode 100755 index 0000000000000..500372eb91596 --- /dev/null +++ b/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -euo pipefail + +ts-node .buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.ts diff --git a/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.test.ts b/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.test.ts new file mode 100644 index 0000000000000..45475301f1c49 --- /dev/null +++ b/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.test.ts @@ -0,0 +1,77 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ +/* eslint-disable @typescript-eslint/no-unused-expressions */ + +import { getVersionsFile } from '#pipeline-utils'; +import { expect } from 'chai'; + +import { + getArtifactBuildTriggers, + getArtifactSnapshotPipelineTriggers, + getESForwardPipelineTriggers, + getArtifactStagingPipelineTriggers, +} from './pipeline'; + +const versionsFile = getVersionsFile(); + +describe('pipeline trigger combinations', () => { + it('should trigger the correct pipelines for "es-forward"', () => { + const esForwardTriggers = getESForwardPipelineTriggers(); + // tests 7.17 against 8.x versions + const targets = versionsFile.versions.filter((v) => v.currentMajor === true); + + expect(esForwardTriggers.length).to.eql(targets.length); + + expect(esForwardTriggers.every((trigger) => trigger.build?.branch === '7.17')).to.be.true; + + const targetedManifests = esForwardTriggers.map((t) => t.build?.env?.ES_SNAPSHOT_MANIFEST); + targets.forEach((t) => + expect(targetedManifests).to.include( + `https://storage.googleapis.com/kibana-ci-es-snapshots-daily/${t.version}/manifest-latest-verified.json` + ) + ); + }); + + it('should trigger the correct pipelines for "artifacts-snapshot"', () => { + const snapshotTriggers = getArtifactSnapshotPipelineTriggers(); + // triggers for all open branches + const branches = versionsFile.versions.map((v) => v.branch); + + expect(snapshotTriggers.length).to.eql(branches.length); + + branches.forEach((b) => { + expect(snapshotTriggers.some((trigger) => trigger.build?.branch === b)).to.be.true; + }); + }); + + it('should trigger the correct pipelines for "artifacts-trigger"', () => { + const triggerTriggers = getArtifactBuildTriggers(); + // all currentMajor+prevMinor branches + const branches = versionsFile.versions + .filter((v) => v.currentMajor === true && v.previousMinor === true) + .map((v) => v.branch); + + expect(triggerTriggers.length).to.eql(branches.length); + branches.forEach((b) => { + expect(triggerTriggers.some((trigger) => trigger.build?.branch === b)).to.be.true; + }); + }); + + it('should trigger the correct pipelines for "artifacts-staging"', () => { + const stagingTriggers = getArtifactStagingPipelineTriggers(); + // all branches that are not currentMajor+currentMinor + const branches = versionsFile.versions + .filter((v) => !v.currentMajor || !v.currentMinor) + .map((v) => v.branch); + + expect(stagingTriggers.length).to.eql(branches.length); + branches.forEach((b) => { + expect(stagingTriggers.some((trigger) => trigger.build?.branch === b)).to.be.true; + }); + }); +}); diff --git a/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.ts b/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.ts new file mode 100755 index 0000000000000..314e3d2164688 --- /dev/null +++ b/.buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.ts @@ -0,0 +1,179 @@ +#!/usr/bin/env ts-node +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { getVersionsFile, BuildkiteTriggerStep } from '#pipeline-utils'; + +const pipelineSets = { + 'es-forward': 'kibana-es-forward-compatibility-testing', + 'artifacts-snapshot': 'kibana-artifacts-snapshot', + 'artifacts-staging': 'kibana-artifacts-staging', + 'artifacts-trigger': 'kibana-artifacts-trigger', +}; + +/** + * This pipeline is used to emit trigger steps onto different pipelines, based on dynamic parameters retrieved from the repository. + */ +async function main() { + const pipelineSetNames = Object.keys(pipelineSets); + const pipelineSetName: string | undefined = process.env.TRIGGER_PIPELINE_SET; + const pipelineSteps: BuildkiteTriggerStep[] = []; + + if (!pipelineSetName) { + throw new Error( + `Env var TRIGGER_PIPELINE_SET is required, and can be one of: ${pipelineSetNames}` + ); + } else if (!pipelineSetNames.includes(pipelineSetName)) { + throw new Error( + `Invalid value for TRIGGER_PIPELINE_SET (${pipelineSetName}), can be one of: ${pipelineSetNames}` + ); + } + + switch (pipelineSetName) { + case 'es-forward': { + pipelineSteps.push(...getESForwardPipelineTriggers()); + break; + } + case 'artifacts-snapshot': { + pipelineSteps.push(...getArtifactSnapshotPipelineTriggers()); + break; + } + case 'artifacts-staging': { + pipelineSteps.push(...getArtifactStagingPipelineTriggers()); + break; + } + case 'artifacts-trigger': { + pipelineSteps.push(...getArtifactBuildTriggers()); + break; + } + default: { + throw new Error(`Unknown pipeline set: ${pipelineSetName}`); + } + } + + emitPipeline(pipelineSteps); +} + +/** + * This pipeline is testing the forward compatibility of Kibana with different versions of Elasticsearch. + * Should be triggered for combinations of (Kibana@7.17 + ES@8.x {current open branches on the same major}) + */ +export function getESForwardPipelineTriggers(): BuildkiteTriggerStep[] { + const versions = getVersionsFile(); + const kibanaPrevMajor = versions.prevMajors[0]; + const targetESVersions = [versions.prevMinors, versions.current].flat(); + + return targetESVersions.map(({ version }) => { + return { + trigger: pipelineSets['es-forward'], + async: true, + label: `Triggering Kibana ${kibanaPrevMajor.version} + ES ${version} forward compatibility`, + build: { + message: process.env.MESSAGE || `ES forward-compatibility test for ES ${version}`, + branch: kibanaPrevMajor.branch, + commit: 'HEAD', + env: { + ES_SNAPSHOT_MANIFEST: `https://storage.googleapis.com/kibana-ci-es-snapshots-daily/${version}/manifest-latest-verified.json`, + DRY_RUN: process.env.DRY_RUN, + }, + }, + } as BuildkiteTriggerStep; + }); +} + +/** + * This pipeline creates Kibana artifact snapshots for all open branches. + * Should be triggered for all open branches in the versions.json: 7.x, 8.x + */ +export function getArtifactSnapshotPipelineTriggers() { + // Trigger for all named branches + const versions = getVersionsFile(); + const targetVersions = [versions.prevMajors, versions.prevMinors, versions.current].flat(); + + return targetVersions.map(({ branch }) => { + return { + trigger: pipelineSets['artifacts-snapshot'], + async: true, + label: `Triggering snapshot artifact builds for ${branch}`, + build: { + message: process.env.MESSAGE || `Snapshot artifact build for ${branch}`, + branch, + commit: 'HEAD', + env: { + DRY_RUN: process.env.DRY_RUN, + }, + }, + } as BuildkiteTriggerStep; + }); +} + +/** + * This pipeline creates Kibana artifacts for branches that are not the current main. + * Should be triggered for all open branches in the versions.json: 7.x, 8.x, but not main. + */ +export function getArtifactStagingPipelineTriggers() { + // Trigger for all branches, that are not current minor+major + const versions = getVersionsFile(); + const targetVersions = [versions.prevMajors, versions.prevMinors].flat(); + + return targetVersions.map(({ branch }) => { + return { + trigger: pipelineSets['artifacts-staging'], + async: true, + label: `Triggering staging artifact builds for ${branch}`, + build: { + message: process.env.MESSAGE || `Staging artifact build for ${branch}`, + branch, + commit: 'HEAD', + env: { + DRY_RUN: process.env.DRY_RUN, + }, + }, + } as BuildkiteTriggerStep; + }); +} + +/** + * This pipeline checks if there are any changes in the incorporated $BEATS_MANIFEST_LATEST_URL (beats version) + * and triggers a staging artifact build. + * Should be triggered only for the active minor versions that are not `main` and not `7.17`. + * + * TODO: we could basically do the check logic of .buildkite/scripts/steps/artifacts/trigger.sh in here, and remove kibana-artifacts-trigger + */ +export function getArtifactBuildTriggers() { + const versions = getVersionsFile(); + const targetVersions = versions.prevMinors; + + return targetVersions.map( + ({ branch }) => + ({ + trigger: pipelineSets['artifacts-trigger'], + async: true, + label: `Triggering artifact build for ${branch}`, + build: { + message: process.env.MESSAGE || `Artifact build for ${branch}`, + branch, + commit: 'HEAD', + env: { + DRY_RUN: process.env.DRY_RUN, + }, + }, + } as BuildkiteTriggerStep) + ); +} + +function emitPipeline(pipelineSteps: BuildkiteTriggerStep[]) { + console.log(JSON.stringify(pipelineSteps, null, 2)); +} + +if (require.main === module) { + main().catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/.buildkite/scripts/steps/artifacts/cloud.sh b/.buildkite/scripts/steps/artifacts/cloud.sh index e12cf7958c86e..86fa86f37bd3c 100644 --- a/.buildkite/scripts/steps/artifacts/cloud.sh +++ b/.buildkite/scripts/steps/artifacts/cloud.sh @@ -7,6 +7,11 @@ set -euo pipefail source "$(dirname "$0")/../../common/util.sh" source .buildkite/scripts/steps/artifacts/env.sh +if [[ "${DRY_RUN:-}" =~ ^(true|1)$ ]]; then + echo "--- Nothing to do in DRY_RUN mode" + exit 0 +fi + echo "--- Push docker image" mkdir -p target diff --git a/.buildkite/scripts/steps/artifacts/publish.sh b/.buildkite/scripts/steps/artifacts/publish.sh index 40ea04fc33fea..08c6ecc1e25ad 100644 --- a/.buildkite/scripts/steps/artifacts/publish.sh +++ b/.buildkite/scripts/steps/artifacts/publish.sh @@ -5,6 +5,8 @@ set -euo pipefail source .buildkite/scripts/common/util.sh source .buildkite/scripts/steps/artifacts/env.sh +print_if_dry_run + echo "--- Download and verify artifacts" function download { download_artifact "$1" . --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" @@ -60,6 +62,7 @@ if [[ "$BUILDKITE_BRANCH" == "$KIBANA_BASE_BRANCH" ]]; then download_artifact beats_manifest.json /tmp --build "${KIBANA_BUILD_ID:-$BUILDKITE_BUILD_ID}" export BEATS_MANIFEST_URL=$(jq -r .manifest_url /tmp/beats_manifest.json) + PUBLISH_CMD=$(cat << EOF docker run --rm \ --name release-manager \ -e VAULT_ADDR \ @@ -76,6 +79,13 @@ if [[ "$BUILDKITE_BRANCH" == "$KIBANA_BASE_BRANCH" ]]; then --qualifier "$VERSION_QUALIFIER" \ --dependency "beats:$BEATS_MANIFEST_URL" \ --artifact-set main +EOF +) + if [[ "${DRY_RUN:-}" =~ ^(1|true)$ ]]; then + PUBLISH_CMD+=(" --dry-run") + fi + + "${PUBLISH_CMD[@]}" KIBANA_SUMMARY=$(curl -s "$KIBANA_MANIFEST_LATEST" | jq -re '.summary_url') diff --git a/.buildkite/scripts/steps/serverless/deploy.sh b/.buildkite/scripts/steps/serverless/deploy.sh index 0c6f52b6f1982..325aadf187b5b 100644 --- a/.buildkite/scripts/steps/serverless/deploy.sh +++ b/.buildkite/scripts/steps/serverless/deploy.sh @@ -160,9 +160,12 @@ EOF is_pr_with_label "ci:project-deploy-elasticsearch" && deploy "elasticsearch" if is_pr_with_label "ci:project-deploy-observability" ; then - create_github_issue_oblt_test_environments - echo "--- Deploy observability with Kibana CI" - deploy "observability" + # Only deploy observability if the PR is targeting main + if [[ "$BUILDKITE_PULL_REQUEST_BASE_BRANCH" == "main" ]]; then + create_github_issue_oblt_test_environments + echo "--- Deploy observability with Kibana CI" + deploy "observability" + fi fi is_pr_with_label "ci:project-deploy-security" && deploy "security" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8e93aeee0dae1..bb9fa9d83a5cd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -407,6 +407,7 @@ packages/kbn-eslint-plugin-imports @elastic/kibana-operations packages/kbn-eslint-plugin-telemetry @elastic/obs-knowledge-team examples/eso_model_version_example @elastic/kibana-security x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security +src/plugins/esql @elastic/kibana-esql packages/kbn-esql-ast @elastic/kibana-esql examples/esql_ast_inspector @elastic/kibana-esql src/plugins/esql_datagrid @elastic/kibana-esql @@ -422,7 +423,7 @@ x-pack/plugins/event_log @elastic/response-ops packages/kbn-expandable-flyout @elastic/security-threat-hunting-investigations packages/kbn-expect @elastic/kibana-operations @elastic/appex-qa x-pack/examples/exploratory_view_example @elastic/obs-ux-infra_services-team -x-pack/plugins/observability_solution/exploratory_view @elastic/obs-ux-infra_services-team +x-pack/plugins/observability_solution/exploratory_view @elastic/obs-ux-management-team src/plugins/expression_error @elastic/kibana-presentation src/plugins/chart_expressions/expression_gauge @elastic/kibana-visualizations src/plugins/chart_expressions/expression_heatmap @elastic/kibana-visualizations @@ -505,7 +506,7 @@ x-pack/plugins/integration_assistant @elastic/security-solution src/plugins/interactive_setup @elastic/kibana-security test/interactive_setup_api_integration/plugins/test_endpoints @elastic/kibana-security packages/kbn-interpreter @elastic/kibana-visualizations -x-pack/plugins/observability_solution/investigate @elastic/obs-ai-assistant +x-pack/plugins/observability_solution/investigate @elastic/obs-ux-management-team packages/kbn-io-ts-utils @elastic/obs-knowledge-team packages/kbn-ipynb @elastic/search-kibana packages/kbn-jest-serializers @elastic/kibana-operations @@ -658,6 +659,7 @@ packages/react/kibana_context/root @elastic/appex-sharedux packages/react/kibana_context/styled @elastic/appex-sharedux packages/react/kibana_context/theme @elastic/appex-sharedux packages/react/kibana_mount @elastic/appex-sharedux +packages/kbn-recently-accessed @elastic/appex-sharedux x-pack/plugins/remote_clusters @elastic/kibana-management test/plugin_functional/plugins/rendering_plugin @elastic/kibana-core packages/kbn-repo-file-maps @elastic/kibana-operations @@ -848,7 +850,7 @@ test/server_integration/plugins/status_plugin_b @elastic/kibana-core packages/kbn-std @elastic/kibana-core packages/kbn-stdio-dev-helpers @elastic/kibana-operations packages/kbn-storybook @elastic/kibana-operations -x-pack/plugins/observability_solution/synthetics @elastic/obs-ux-infra_services-team +x-pack/plugins/observability_solution/synthetics @elastic/obs-ux-management-team x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture @elastic/response-ops x-pack/test/plugin_api_perf/plugins/task_manager_performance @elastic/response-ops x-pack/plugins/task_manager @elastic/response-ops @@ -865,7 +867,6 @@ packages/kbn-test-jest-helpers @elastic/kibana-operations @elastic/appex-qa packages/kbn-test-subj-selector @elastic/kibana-operations @elastic/appex-qa x-pack/examples/testing_embedded_lens @elastic/kibana-visualizations packages/kbn-text-based-editor @elastic/kibana-esql -src/plugins/text_based_languages @elastic/kibana-esql x-pack/examples/third_party_lens_navigation_prompt @elastic/kibana-visualizations x-pack/examples/third_party_vis_lens_example @elastic/kibana-visualizations x-pack/plugins/threat_intelligence @elastic/security-threat-hunting-investigations @@ -904,7 +905,7 @@ src/plugins/unified_search @elastic/kibana-visualizations packages/kbn-unsaved-changes-badge @elastic/kibana-data-discovery packages/kbn-unsaved-changes-prompt @elastic/kibana-management x-pack/plugins/upgrade_assistant @elastic/kibana-management -x-pack/plugins/observability_solution/uptime @elastic/obs-ux-infra_services-team +x-pack/plugins/observability_solution/uptime @elastic/obs-ux-management-team x-pack/plugins/drilldowns/url_drilldown @elastic/appex-sharedux src/plugins/url_forwarding @elastic/kibana-visualizations src/plugins/usage_collection @elastic/kibana-core @@ -1142,15 +1143,15 @@ x-pack/test/observability_ai_assistant_functional @elastic/obs-ai-assistant #CC# /x-pack/plugins/observability_solution/observability/ @elastic/apm-ui # Uptime -/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/uptime/ @elastic/obs-ux-infra_services-team -/x-pack/test/functional/apps/uptime @elastic/obs-ux-infra_services-team -/x-pack/test/functional/es_archives/uptime @elastic/obs-ux-infra_services-team -/x-pack/test/functional/services/uptime @elastic/obs-ux-infra_services-team -/x-pack/test/api_integration/apis/uptime @elastic/obs-ux-infra_services-team -/x-pack/test/api_integration/apis/synthetics @elastic/obs-ux-infra_services-team -/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts @elastic/obs-ux-infra_services-team +/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/uptime/ @elastic/obs-ux-management-team +/x-pack/test/functional/apps/uptime @elastic/obs-ux-management-team +/x-pack/test/functional/es_archives/uptime @elastic/obs-ux-management-team +/x-pack/test/functional/services/uptime @elastic/obs-ux-management-team +/x-pack/test/api_integration/apis/uptime @elastic/obs-ux-management-team +/x-pack/test/api_integration/apis/synthetics @elastic/obs-ux-management-team +/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts @elastic/obs-ux-management-team /x-pack/test/alerting_api_integration/observability/index.ts @elastic/obs-ux-management-team -/x-pack/test_serverless/api_integration/test_suites/observability/synthetics @elastic/obs-ux-infra_services-team +/x-pack/test_serverless/api_integration/test_suites/observability/synthetics @elastic/obs-ux-management-team # Logs /x-pack/test/api_integration/apis/logs_ui @elastic/obs-ux-logs-team @@ -1734,9 +1735,9 @@ packages/react @elastic/appex-sharedux x-pack/plugins/actions/server/saved_objects/index.ts @elastic/response-ops @elastic/kibana-security x-pack/plugins/alerting/server/saved_objects/index.ts @elastic/response-ops @elastic/kibana-security x-pack/plugins/fleet/server/saved_objects/index.ts @elastic/fleet @elastic/kibana-security -x-pack/plugins/observability_solution/synthetics/server/saved_objects/saved_objects.ts @elastic/obs-ux-infra_services-team @elastic/kibana-security -x-pack/plugins/observability_solution/synthetics/server/saved_objects/synthetics_monitor.ts @elastic/obs-ux-infra_services-team @elastic/kibana-security -x-pack/plugins/observability_solution/synthetics/server/saved_objects/synthetics_param.ts @elastic/obs-ux-infra_services-team @elastic/kibana-security +x-pack/plugins/observability_solution/synthetics/server/saved_objects/saved_objects.ts @elastic/obs-ux-management-team @elastic/kibana-security +x-pack/plugins/observability_solution/synthetics/server/saved_objects/synthetics_monitor.ts @elastic/obs-ux-management-team @elastic/kibana-security +x-pack/plugins/observability_solution/synthetics/server/saved_objects/synthetics_param.ts @elastic/obs-ux-management-team @elastic/kibana-security # Specialised GitHub workflows for the Observability robots /.github/workflows/deploy-my-kibana.yml @elastic/observablt-robots @elastic/kibana-operations diff --git a/.i18nrc.json b/.i18nrc.json index 63adc5c5aae2b..bab7cdc68d81d 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -116,7 +116,7 @@ "serverlessPackages": "packages/serverless", "coloring": "packages/kbn-coloring/src", "languageDocumentationPopover": "packages/kbn-language-documentation-popover/src", - "textBasedLanguages": "src/plugins/text_based_languages", + "esql": "src/plugins/esql", "esqlDataGrid": "src/plugins/esql_datagrid", "statusPage": "src/legacy/core_plugins/status_page", "telemetry": ["src/plugins/telemetry", "src/plugins/telemetry_management_section"], diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 096066e0ce5bd..b6fffb4194b6b 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 56e1e3c05dc9c..6492dd6984167 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index 59d2a88975220..8d2b1f6fef27c 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 6c0a62c9c1341..729b2feaf6311 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 39480138681d2..830bc1ebcdbe3 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index c3150722d0adb..3e3688dce2c04 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 9f794fe86c6b7..21b67dd6160cc 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/assets_data_access.mdx b/api_docs/assets_data_access.mdx index f82175b8f2b38..3ea04a6401275 100644 --- a/api_docs/assets_data_access.mdx +++ b/api_docs/assets_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetsDataAccess title: "assetsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the assetsDataAccess plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetsDataAccess'] --- import assetsDataAccessObj from './assets_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 5614bdf511a31..ecf938f654271 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 5eb1c8f800a49..a9ea60f5a4f42 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index a85993e163b53..24d534fd65a08 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index e4106724469ca..e322a93eaebd9 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index e34208dde9c2d..de9a785c6f3ed 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 803baa19d1b0d..f502cdf61f118 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 688d775419638..1e031cba557ed 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index fb38869a0e18a..20c6b22cbb41b 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 8c6deb3977330..ae517fc661661 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 4ed908f53f278..847acbfa0de75 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 02950d752e811..0825074a8d879 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index d60d0a1fbb45b..eb4b9b449e09b 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index f8470abe439d0..ed3402a2c87db 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index e57c81077c45e..f93ee1c9104b1 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 8f8ca8f4e7700..bf7dabe1a8958 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 1bcb7f25a63c5..8621e73a61b7a 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index a9cef4ffe9dce..1b14f907dc075 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_quality.devdocs.json b/api_docs/data_quality.devdocs.json index 9ed104a5f220d..1abc27872df38 100644 --- a/api_docs/data_quality.devdocs.json +++ b/api_docs/data_quality.devdocs.json @@ -47,70 +47,9 @@ "common": { "classes": [], "functions": [], - "interfaces": [ - { - "parentPluginId": "dataQuality", - "id": "def-common.DataQualityLocatorParams", - "type": "Interface", - "tags": [], - "label": "DataQualityLocatorParams", - "description": [], - "signature": [ - { - "pluginId": "dataQuality", - "scope": "common", - "docId": "kibDataQualityPluginApi", - "section": "def-common.DataQualityLocatorParams", - "text": "DataQualityLocatorParams" - }, - " extends ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - } - ], - "path": "x-pack/plugins/data_quality/common/locators/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "dataQuality", - "id": "def-common.DataQualityLocatorParams.filters", - "type": "Object", - "tags": [], - "label": "filters", - "description": [], - "signature": [ - "Filters | undefined" - ], - "path": "x-pack/plugins/data_quality/common/locators/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - } - ], + "interfaces": [], "enums": [], "misc": [ - { - "parentPluginId": "dataQuality", - "id": "def-common.DATA_QUALITY_LOCATOR_ID", - "type": "string", - "tags": [], - "label": "DATA_QUALITY_LOCATOR_ID", - "description": [], - "signature": [ - "\"DATA_QUALITY_LOCATOR\"" - ], - "path": "x-pack/plugins/data_quality/common/locators/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "dataQuality", "id": "def-common.DATA_QUALITY_URL_STATE_KEY", diff --git a/api_docs/data_quality.mdx b/api_docs/data_quality.mdx index 7b4ccf434823e..e3303471571cd 100644 --- a/api_docs/data_quality.mdx +++ b/api_docs/data_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataQuality title: "dataQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the dataQuality plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataQuality'] --- import dataQualityObj from './data_quality.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 8 | 0 | 8 | 0 | +| 5 | 0 | 5 | 0 | ## Client @@ -33,9 +33,6 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux ## Common -### Interfaces - - ### Consts, variables and types diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 8de4d0592f7ea..234d0de08b169 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 37f8ceb15cc78..174dfd5a5f487 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index f3807615bcee7..2d73601ae56a8 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index dd4529822585a..ea063ed0ddeeb 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index d0b434a81c508..477ea83571529 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 0b0e2fb4147bb..611d75c419502 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -12992,6 +12992,10 @@ "plugin": "controls", "path": "src/plugins/controls/public/services/options_list/options_list_service.ts" }, + { + "plugin": "@kbn/lens-embeddable-utils", + "path": "packages/kbn-lens-embeddable-utils/config_builder/columns/breakdown.ts" + }, { "plugin": "triggersActionsUi", "path": "x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts" @@ -13116,10 +13120,6 @@ "plugin": "ml", "path": "x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/source_selection/source_selection.tsx" }, - { - "plugin": "@kbn/lens-embeddable-utils", - "path": "packages/kbn-lens-embeddable-utils/config_builder/columns/breakdown.ts" - }, { "plugin": "@kbn/ml-data-view-utils", "path": "x-pack/packages/ml/data_view_utils/actions/data_view_handler.ts" diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index f70820a6b113c..10a355586b1b7 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 05b840fe8c3d3..fb4dbd51a2442 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index be1082197b1d3..4dabd7b5e9c45 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index f2079fd416fad..a07ed2211d123 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -17,7 +17,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Referencing plugin(s) | Remove By | | ---------------|-----------|-----------| | | ml, stackAlerts | - | -| | data, @kbn/search-errors, savedObjectsManagement, unifiedSearch, @kbn/unified-field-list, lens, controls, triggersActionsUi, dataVisualizer, canvas, presentationUtil, logsShared, fleet, ml, @kbn/lens-embeddable-utils, @kbn/ml-data-view-utils, enterpriseSearch, graph, visTypeTimeseries, exploratoryView, stackAlerts, infra, securitySolution, timelines, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, eventAnnotationListing, inputControlVis, visDefaultEditor, visTypeTimelion, visTypeVega | - | +| | data, @kbn/search-errors, savedObjectsManagement, unifiedSearch, @kbn/unified-field-list, lens, controls, @kbn/lens-embeddable-utils, triggersActionsUi, dataVisualizer, canvas, presentationUtil, logsShared, fleet, ml, @kbn/ml-data-view-utils, enterpriseSearch, graph, visTypeTimeseries, exploratoryView, stackAlerts, infra, securitySolution, timelines, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, eventAnnotationListing, inputControlVis, visDefaultEditor, visTypeTimelion, visTypeVega | - | | | ml, securitySolution | - | | | actions, savedObjectsTagging, ml, enterpriseSearch | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core, savedObjects, visualizations, aiops, dataVisualizer, ml, dashboardEnhanced, graph, lens, securitySolution, eventAnnotation, @kbn/core-saved-objects-browser-mocks | - | @@ -39,7 +39,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | cloudDefend, osquery, securitySolution, synthetics | - | | | cloudDefend, osquery, securitySolution, synthetics | - | -| | alerting, observabilityAIAssistant, fleet, cloudSecurityPosture, enterpriseSearch, securitySolution, serverlessSearch, transform, upgradeAssistant, apm, entityManager, observabilityOnboarding, synthetics, security | - | | | cases, securitySolution, security | - | | | @kbn/securitysolution-data-table, securitySolution | - | | | @kbn/securitysolution-data-table, securitySolution | - | @@ -66,6 +65,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | @kbn/monaco, securitySolution | - | | | fleet, cloudSecurityPosture, exploratoryView, osquery, synthetics | - | +| | alerting, observabilityAIAssistant, fleet, cloudSecurityPosture, enterpriseSearch, serverlessSearch, transform, upgradeAssistant, apm, entityManager, observabilityOnboarding, synthetics, security | - | | | actions, alerting | - | | | visualizations, lens, controls, dashboard, discover | - | | | discover, @kbn/reporting-public | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 3d6bec3acdf4b..f1fb99dcd5ae9 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -1328,8 +1328,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode)+ 7 more | 8.8.0 | | | [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [policy_config.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/common/license/policy_config.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [fleet_integration.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [create_default_policy.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/fleet_integration/handlers/create_default_policy.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode), [license_watch.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts#:~:text=mode)+ 7 more | 8.8.0 | | | [get_is_alert_suppression_active.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_is_alert_suppression_active.ts#:~:text=license%24), [create_threat_signals.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts#:~:text=license%24), [query.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts#:~:text=license%24), [threshold.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts#:~:text=license%24), [get_is_alert_suppression_active.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_is_alert_suppression_active.test.ts#:~:text=license%24), [get_is_alert_suppression_active.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_is_alert_suppression_active.test.ts#:~:text=license%24), [get_is_alert_suppression_active.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_is_alert_suppression_active.test.ts#:~:text=license%24) | 8.8.0 | -| | [request_context_factory.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/request_context_factory.ts#:~:text=authc), [route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts#:~:text=authc), [create_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts#:~:text=authc), [delete_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts#:~:text=authc), [finalize_signals_migration_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts#:~:text=authc), [common.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts#:~:text=authc) | - | -| | [endpoint_app_context_services.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts#:~:text=authc), [open_close_signals_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts#:~:text=authc), [open_close_signals_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts#:~:text=authc), [file_info_handler.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts#:~:text=authc), [file_download_handler.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts#:~:text=authc), [response_actions.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts#:~:text=authc), [list.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/list.test.ts#:~:text=authc), [response_actions.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts#:~:text=authc), [state.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.test.ts#:~:text=authc), [index.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.test.ts#:~:text=authc)+ 14 more | - | +| | [route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts#:~:text=authc) | - | | | [suggest_user_profiles_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/users/suggest_user_profiles_route.ts#:~:text=userProfiles), [suggest_user_profiles_route.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/routes/users/suggest_user_profiles_route.ts#:~:text=userProfiles) | - | | | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx#:~:text=DeprecatedCellValueElementProps), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx#:~:text=DeprecatedCellValueElementProps) | - | | | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx#:~:text=DeprecatedRowRenderer), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx#:~:text=DeprecatedRowRenderer) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index ba8f747f2b66b..2cc94d7fa75f6 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index e19d6a9775354..562287dd1cb42 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index c8feab13c40f0..90f85106e8612 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index aeb8137f43e93..053a1991eb618 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index f68e0f66f9d2d..231fe7f410a82 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index ee89b9fcaa377..a8a4ee382dab3 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index b9f54e85c98a3..fccf3d6b64831 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 33b17b1b3e084..ba0258d8589df 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index a9019137412ae..6ea23c886a0f7 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index cf1cb411d4e83..5b3f5a9f72c75 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 38f98bde1d9b5..0b4ee2c5256d1 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/entity_manager.devdocs.json b/api_docs/entity_manager.devdocs.json index f8f2a8dde90aa..145b8f97ff63a 100644 --- a/api_docs/entity_manager.devdocs.json +++ b/api_docs/entity_manager.devdocs.json @@ -77,6 +77,96 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "entityManager", + "id": "def-public.ERROR_API_KEY_NOT_FOUND", + "type": "string", + "tags": [], + "label": "ERROR_API_KEY_NOT_FOUND", + "description": [], + "signature": [ + "\"api_key_not_found\"" + ], + "path": "x-pack/plugins/observability_solution/entity_manager/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "entityManager", + "id": "def-public.ERROR_API_KEY_NOT_VALID", + "type": "string", + "tags": [], + "label": "ERROR_API_KEY_NOT_VALID", + "description": [], + "signature": [ + "\"api_key_not_valid\"" + ], + "path": "x-pack/plugins/observability_solution/entity_manager/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "entityManager", + "id": "def-public.ERROR_API_KEY_SERVICE_DISABLED", + "type": "string", + "tags": [], + "label": "ERROR_API_KEY_SERVICE_DISABLED", + "description": [], + "signature": [ + "\"api_key_service_disabled\"" + ], + "path": "x-pack/plugins/observability_solution/entity_manager/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "entityManager", + "id": "def-public.ERROR_DEFINITION_STOPPED", + "type": "string", + "tags": [], + "label": "ERROR_DEFINITION_STOPPED", + "description": [], + "signature": [ + "\"error_definition_stopped\"" + ], + "path": "x-pack/plugins/observability_solution/entity_manager/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "entityManager", + "id": "def-public.ERROR_PARTIAL_BUILTIN_INSTALLATION", + "type": "string", + "tags": [], + "label": "ERROR_PARTIAL_BUILTIN_INSTALLATION", + "description": [], + "signature": [ + "\"partial_builtin_installation\"" + ], + "path": "x-pack/plugins/observability_solution/entity_manager/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "entityManager", + "id": "def-public.ERROR_USER_NOT_AUTHORIZED", + "type": "string", + "tags": [], + "label": "ERROR_USER_NOT_AUTHORIZED", + "description": [], + "signature": [ + "\"user_not_authorized\"" + ], + "path": "x-pack/plugins/observability_solution/entity_manager/common/errors.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "objects": [] diff --git a/api_docs/entity_manager.mdx b/api_docs/entity_manager.mdx index f85d2feea348f..359efb8f409c7 100644 --- a/api_docs/entity_manager.mdx +++ b/api_docs/entity_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entityManager title: "entityManager" image: https://source.unsplash.com/400x175/?github description: API docs for the entityManager plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entityManager'] --- import entityManagerObj from './entity_manager.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 8 | 0 | 8 | 1 | +| 14 | 0 | 14 | 1 | ## Client diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index ad72afddd7a28..7e4c7ae8ed8e7 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/esql.devdocs.json b/api_docs/esql.devdocs.json new file mode 100644 index 0000000000000..f3d9e7db3931c --- /dev/null +++ b/api_docs/esql.devdocs.json @@ -0,0 +1,547 @@ +{ + "id": "esql", + "client": { + "classes": [], + "functions": [ + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLangEditor", + "type": "Function", + "tags": [], + "label": "TextBasedLangEditor", + "description": [], + "signature": [ + "(props: ", + { + "pluginId": "@kbn/text-based-editor", + "scope": "public", + "docId": "kibKbnTextBasedEditorPluginApi", + "section": "def-public.TextBasedLanguagesEditorProps", + "text": "TextBasedLanguagesEditorProps" + }, + ") => JSX.Element" + ], + "path": "src/plugins/esql/public/create_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLangEditor.$1", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "signature": [ + { + "pluginId": "@kbn/text-based-editor", + "scope": "public", + "docId": "kibKbnTextBasedEditorPluginApi", + "section": "def-public.TextBasedLanguagesEditorProps", + "text": "TextBasedLanguagesEditorProps" + } + ], + "path": "src/plugins/esql/public/create_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "esql", + "id": "def-public.EsqlPluginStart", + "type": "Interface", + "tags": [], + "label": "EsqlPluginStart", + "description": [], + "path": "src/plugins/esql/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "esql", + "id": "def-public.EsqlPluginStart.Editor", + "type": "CompoundType", + "tags": [], + "label": "Editor", + "description": [], + "signature": [ + "React.ComponentClass<", + { + "pluginId": "@kbn/text-based-editor", + "scope": "public", + "docId": "kibKbnTextBasedEditorPluginApi", + "section": "def-public.TextBasedLanguagesEditorProps", + "text": "TextBasedLanguagesEditorProps" + }, + ", any> | React.FunctionComponent<", + { + "pluginId": "@kbn/text-based-editor", + "scope": "public", + "docId": "kibKbnTextBasedEditorPluginApi", + "section": "def-public.TextBasedLanguagesEditorProps", + "text": "TextBasedLanguagesEditorProps" + }, + ">" + ], + "path": "src/plugins/esql/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps", + "type": "Interface", + "tags": [], + "label": "TextBasedLanguagesEditorProps", + "description": [], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.query", + "type": "Object", + "tags": [], + "label": "query", + "description": [ + "The aggregate type query" + ], + "signature": [ + "{ esql: string; }" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.onTextLangQueryChange", + "type": "Function", + "tags": [], + "label": "onTextLangQueryChange", + "description": [ + "Callback running everytime the query changes" + ], + "signature": [ + "(query: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + ") => void" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.onTextLangQueryChange.$1", + "type": "Object", + "tags": [], + "label": "query", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + } + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.onTextLangQuerySubmit", + "type": "Function", + "tags": [], + "label": "onTextLangQuerySubmit", + "description": [ + "Callback running when the user submits the query" + ], + "signature": [ + "(query?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + " | undefined, abortController?: AbortController | undefined) => Promise" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.onTextLangQuerySubmit.$1", + "type": "Object", + "tags": [], + "label": "query", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.AggregateQuery", + "text": "AggregateQuery" + }, + " | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.onTextLangQuerySubmit.$2", + "type": "Object", + "tags": [], + "label": "abortController", + "description": [], + "signature": [ + "AbortController | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.expandCodeEditor", + "type": "Function", + "tags": [], + "label": "expandCodeEditor", + "description": [ + "Can be used to expand/minimize the editor" + ], + "signature": [ + "(status: boolean) => void" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.expandCodeEditor.$1", + "type": "boolean", + "tags": [], + "label": "status", + "description": [], + "signature": [ + "boolean" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.isCodeEditorExpanded", + "type": "boolean", + "tags": [], + "label": "isCodeEditorExpanded", + "description": [ + "If it is true, the editor initializes with height EDITOR_INITIAL_HEIGHT_EXPANDED" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.detectTimestamp", + "type": "CompoundType", + "tags": [], + "label": "detectTimestamp", + "description": [ + "If it is true, the editor displays the message @timestamp found\nThe text based queries are relying on adhoc dataviews which\ncan have an @timestamp timefield or nothing" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.errors", + "type": "Array", + "tags": [], + "label": "errors", + "description": [ + "Array of errors" + ], + "signature": [ + "Error[] | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.warning", + "type": "string", + "tags": [], + "label": "warning", + "description": [ + "Warning string as it comes from ES" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.isLoading", + "type": "CompoundType", + "tags": [], + "label": "isLoading", + "description": [ + "Disables the editor and displays loading icon in run button\nIt is also used for hiding the history component if it is not defined" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.isDisabled", + "type": "CompoundType", + "tags": [], + "label": "isDisabled", + "description": [ + "Disables the editor" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.isDarkMode", + "type": "CompoundType", + "tags": [], + "label": "isDarkMode", + "description": [ + "Indicator if the editor is on dark mode" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.dataTestSubj", + "type": "string", + "tags": [], + "label": "dataTestSubj", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.hideMinimizeButton", + "type": "CompoundType", + "tags": [], + "label": "hideMinimizeButton", + "description": [ + "If true it hides the minimize button and the user can't return to the minimized version\nUseful when the application doesn't want to give this capability" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.hideRunQueryText", + "type": "CompoundType", + "tags": [], + "label": "hideRunQueryText", + "description": [ + "Hide the Run query information which appears on the footer" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.editorIsInline", + "type": "CompoundType", + "tags": [], + "label": "editorIsInline", + "description": [ + "This is used for applications (such as the inline editing flyout in dashboards)\nwhich want to add the editor without being part of the Unified search component\nIt renders a submit query button inside the editor" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.disableSubmitAction", + "type": "CompoundType", + "tags": [], + "label": "disableSubmitAction", + "description": [ + "Disables the submit query action" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.allowQueryCancellation", + "type": "CompoundType", + "tags": [], + "label": "allowQueryCancellation", + "description": [ + "when set to true enables query cancellation" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.hideTimeFilterInfo", + "type": "CompoundType", + "tags": [], + "label": "hideTimeFilterInfo", + "description": [ + "hide @timestamp info" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.hideQueryHistory", + "type": "CompoundType", + "tags": [], + "label": "hideQueryHistory", + "description": [ + "hide query history" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "esql", + "id": "def-public.TextBasedLanguagesEditorProps.hideHeaderWhenExpanded", + "type": "CompoundType", + "tags": [], + "label": "hideHeaderWhenExpanded", + "description": [ + "hide header buttons when editor is expanded" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-text-based-editor/src/text_based_languages_editor.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/esql.mdx b/api_docs/esql.mdx new file mode 100644 index 0000000000000..8d8b7f0c0b679 --- /dev/null +++ b/api_docs/esql.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibEsqlPluginApi +slug: /kibana-dev-docs/api/esql +title: "esql" +image: https://source.unsplash.com/400x175/?github +description: API docs for the esql plugin +date: 2024-07-11 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esql'] +--- +import esqlObj from './esql.devdocs.json'; + + + +Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 29 | 0 | 10 | 0 | + +## Client + +### Functions + + +### Interfaces + + diff --git a/api_docs/esql_data_grid.mdx b/api_docs/esql_data_grid.mdx index 3f2c0a4d9c670..ca621bfee4b02 100644 --- a/api_docs/esql_data_grid.mdx +++ b/api_docs/esql_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esqlDataGrid title: "esqlDataGrid" image: https://source.unsplash.com/400x175/?github description: API docs for the esqlDataGrid plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esqlDataGrid'] --- import esqlDataGridObj from './esql_data_grid.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 8b68077abbcc7..7427bc363bf0e 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index 2e26b580d7ab1..a65650e5685ce 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 5dceb029dd291..0154024c4e5f2 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 6ace7f03b7e98..2cd5df3e65687 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; -Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) for questions regarding this plugin. +Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index ae10832e86007..61400637bd16c 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 60112b987fa25..1add6ebb11b99 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 8d28641ef9110..a46147400ad60 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 24380d9cb7a28..5de5767ec431d 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 8c2aa4cbee4bf..5478834bbe46c 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 55f8f70299ab9..c513aec5c05f0 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.devdocs.json b/api_docs/expression_metric_vis.devdocs.json index 3212eb93a18fe..920c0a7b141d0 100644 --- a/api_docs/expression_metric_vis.devdocs.json +++ b/api_docs/expression_metric_vis.devdocs.json @@ -449,6 +449,62 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.titlesTextAlign", + "type": "CompoundType", + "tags": [], + "label": "titlesTextAlign", + "description": [], + "signature": [ + "\"right\" | \"left\" | \"center\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.valuesTextAlign", + "type": "CompoundType", + "tags": [], + "label": "valuesTextAlign", + "description": [], + "signature": [ + "\"right\" | \"left\" | \"center\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.iconAlign", + "type": "CompoundType", + "tags": [], + "label": "iconAlign", + "description": [], + "signature": [ + "\"right\" | \"left\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.valueFontSize", + "type": "CompoundType", + "tags": [], + "label": "valueFontSize", + "description": [], + "signature": [ + "number | \"default\" | \"fit\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "expressionMetricVis", "id": "def-common.MetricArguments.color", @@ -745,6 +801,62 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.titlesTextAlign", + "type": "CompoundType", + "tags": [], + "label": "titlesTextAlign", + "description": [], + "signature": [ + "\"right\" | \"left\" | \"center\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.valuesTextAlign", + "type": "CompoundType", + "tags": [], + "label": "valuesTextAlign", + "description": [], + "signature": [ + "\"right\" | \"left\" | \"center\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.iconAlign", + "type": "CompoundType", + "tags": [], + "label": "iconAlign", + "description": [], + "signature": [ + "\"right\" | \"left\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.valueFontSize", + "type": "CompoundType", + "tags": [], + "label": "valueFontSize", + "description": [], + "signature": [ + "number | \"default\" | \"fit\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "expressionMetricVis", "id": "def-common.MetricVisParam.maxCols", diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 81a907caa006e..9b335fd928ddc 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 67 | 0 | 67 | 2 | +| 75 | 0 | 75 | 2 | ## Client diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index a3321202f8de6..1bf57bb07c913 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index cb0416e763aab..663c37f0336cd 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index cf12bc2eb835f..883291f327bca 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 8425f9b438d21..7030d0e522164 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 6229db242f0b1..b95138d54ab47 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 5c5befc88992e..908c8f388039c 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 33e5dcb1c9e29..2c7741a342025 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 3623e4b738163..b3e14b5434c3b 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 4aa09771437ed..0f2a1c1d4bd9c 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/fields_metadata.mdx b/api_docs/fields_metadata.mdx index 07e9cc7955981..98e8397418109 100644 --- a/api_docs/fields_metadata.mdx +++ b/api_docs/fields_metadata.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldsMetadata title: "fieldsMetadata" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldsMetadata plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldsMetadata'] --- import fieldsMetadataObj from './fields_metadata.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index d7bb8672d1bbb..3e073cfc2b353 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 336c7b989982d..176485460c7db 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 08229e66440de..0348766c93248 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index fec8ffac9edc5..b0da7aab43721 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -25871,6 +25871,20 @@ "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.RegistryVarsEntry.RegistryVarsEntryKeys.full_width", + "type": "CompoundType", + "tags": [], + "label": "[RegistryVarsEntryKeys.full_width]", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index cf34eb3c65364..e99b668273f28 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1348 | 5 | 1226 | 72 | +| 1349 | 5 | 1227 | 72 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index f673e4f74ddef..d8a4778b5dfb4 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 0f0f71be5ab1f..cd0a584abd942 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 39f790b134299..12cd9e9a7fd41 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 7d008c8cda146..a285787fe67ac 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 2198168f7de1e..d5e4649658817 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 6897a3353fc85..9e81464ca56ea 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 46eb5f1b08eea..20783144b93a1 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 7bda5d0e3d79a..bda1a481df694 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index e17918db58afc..5f4f529bd6388 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/integration_assistant.mdx b/api_docs/integration_assistant.mdx index e508673b99e2e..6d0242e8a2069 100644 --- a/api_docs/integration_assistant.mdx +++ b/api_docs/integration_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/integrationAssistant title: "integrationAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the integrationAssistant plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'integrationAssistant'] --- import integrationAssistantObj from './integration_assistant.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 706fd017d0bd1..c47d63303a0a3 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/investigate.mdx b/api_docs/investigate.mdx index 3564bad1539b1..7722afcb762e4 100644 --- a/api_docs/investigate.mdx +++ b/api_docs/investigate.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/investigate title: "investigate" image: https://source.unsplash.com/400x175/?github description: API docs for the investigate plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigate'] --- import investigateObj from './investigate.devdocs.json'; -Contact [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) for questions regarding this plugin. +Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 59e2e0458b0f1..fc68672cf88f7 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 5cf407eec0949..7036d7e54f77e 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 026905a20e422..bbf1801c680f1 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index cfd1a74d55049..95d4e6f07fcc3 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index cf1d55a4eba09..9e6677bd8b7ae 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 7229c00805c46..684e0747d51b3 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_comparators.mdx b/api_docs/kbn_alerting_comparators.mdx index c15e5c9d98814..64bbc439d1091 100644 --- a/api_docs/kbn_alerting_comparators.mdx +++ b/api_docs/kbn_alerting_comparators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-comparators title: "@kbn/alerting-comparators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-comparators plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-comparators'] --- import kbnAlertingComparatorsObj from './kbn_alerting_comparators.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index c9f4894e32a84..4b29160e5d927 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index a85c7f65b836a..b7bbb6492201a 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 536f700643823..fd2b3675e12b8 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_grouping.mdx b/api_docs/kbn_alerts_grouping.mdx index 046fba053c096..77bdc915dbdc3 100644 --- a/api_docs/kbn_alerts_grouping.mdx +++ b/api_docs/kbn_alerts_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-grouping title: "@kbn/alerts-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-grouping plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-grouping'] --- import kbnAlertsGroupingObj from './kbn_alerts_grouping.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 8fbd9e6f4c10d..fd5677fce9f74 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 20a3a8dc84667..6896546ffb32a 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 61471dadcc39a..1c052b7b6c4d2 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index b03700b40d54c..52f46921b8a6c 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index 679a62fc5ac1f..8c863c11ca3d9 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 9cb8d8f1f5ab8..2e6cd4d8a33c6 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 171175c92c49a..bee339a7a5b96 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index efd66334ac7ba..412a59fff7245 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index d873cf82d0148..4802854b39e3e 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index 8cf1337712acc..d42e2b360aebd 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 91299eadcdb61..a4ccd68d6f248 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index 79b85cddb8cd8..014436b53055d 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 1760c4e447c04..be98393f76557 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 32bf933999b25..1de24a4d18c68 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index ad00bf1686940..3019f08b578a8 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 0d093b4155ebd..8703f4965b812 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index e81db5b2eec23..22576ae2eac24 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 03fdbac88bfd9..d8250347da8aa 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 0211a48eb90f8..d42a537edf4b1 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 9b81e1457bda7..2929a1a2162c3 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 4fbefc50ea6fe..48265e3c6f043 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index ed17d8c6a8df6..e7f9b5fa5f2a6 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index 324b3553b7773..d980134f35105 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 90ce92ea8f905..fe48d5fddc2ff 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 514409c17604b..e6d2d81f5268b 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 6e9b16e4631a6..dc4eeb2fc1564 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index e4dd77810aa0c..91d2e4c2675af 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 879b27192725d..423028ccf543d 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index 0722f7032796a..4668b77ab9918 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.devdocs.json b/api_docs/kbn_content_management_table_list_view.devdocs.json index f3827b9ca8123..2768884c3da7a 100644 --- a/api_docs/kbn_content_management_table_list_view.devdocs.json +++ b/api_docs/kbn_content_management_table_list_view.devdocs.json @@ -19,7 +19,7 @@ "section": "def-common.UserContentCommonSchema", "text": "UserContentCommonSchema" }, - ">({ title, description, entityName, entityNamePlural, initialFilter, headingId, initialPageSize, listingLimit, urlStateEnabled, customTableColumn, emptyPrompt, findItems, createItem, editItem, deleteItems, getDetailViewLink, getOnClickTitle, rowItemActions, id: listingId, contentEditor, children, titleColumnName, additionalRightSideActions, withoutPageTemplateWrapper, createdByEnabled, }: ", + ">({ title, description, entityName, entityNamePlural, initialFilter, headingId, initialPageSize, listingLimit, urlStateEnabled, customTableColumn, emptyPrompt, findItems, createItem, editItem, deleteItems, getDetailViewLink, getOnClickTitle, rowItemActions, id: listingId, contentEditor, children, titleColumnName, additionalRightSideActions, withoutPageTemplateWrapper, createdByEnabled, recentlyAccessed, }: ", { "pluginId": "@kbn/content-management-table-list-view", "scope": "public", @@ -38,7 +38,7 @@ "id": "def-public.TableListView.$1", "type": "CompoundType", "tags": [], - "label": "{\n title,\n description,\n entityName,\n entityNamePlural,\n initialFilter,\n headingId,\n initialPageSize,\n listingLimit,\n urlStateEnabled = true,\n customTableColumn,\n emptyPrompt,\n findItems,\n createItem,\n editItem,\n deleteItems,\n getDetailViewLink,\n getOnClickTitle,\n rowItemActions,\n id: listingId,\n contentEditor,\n children,\n titleColumnName,\n additionalRightSideActions,\n withoutPageTemplateWrapper,\n createdByEnabled,\n}", + "label": "{\n title,\n description,\n entityName,\n entityNamePlural,\n initialFilter,\n headingId,\n initialPageSize,\n listingLimit,\n urlStateEnabled = true,\n customTableColumn,\n emptyPrompt,\n findItems,\n createItem,\n editItem,\n deleteItems,\n getDetailViewLink,\n getOnClickTitle,\n rowItemActions,\n id: listingId,\n contentEditor,\n children,\n titleColumnName,\n additionalRightSideActions,\n withoutPageTemplateWrapper,\n createdByEnabled,\n recentlyAccessed,\n}", "description": [], "signature": [ { @@ -79,7 +79,7 @@ "section": "def-public.TableListViewTableProps", "text": "TableListViewTableProps" }, - ", \"id\" | \"entityName\" | \"entityNamePlural\" | \"initialFilter\" | \"headingId\" | \"initialPageSize\" | \"listingLimit\" | \"urlStateEnabled\" | \"customTableColumn\" | \"emptyPrompt\" | \"findItems\" | \"createItem\" | \"editItem\" | \"deleteItems\" | \"getDetailViewLink\" | \"getOnClickTitle\" | \"rowItemActions\" | \"contentEditor\" | \"titleColumnName\" | \"withoutPageTemplateWrapper\" | \"createdByEnabled\"> & { title: string; description?: string | undefined; additionalRightSideActions?: React.ReactNode[] | undefined; children?: React.ReactNode; }" + ", \"id\" | \"entityName\" | \"entityNamePlural\" | \"initialFilter\" | \"headingId\" | \"initialPageSize\" | \"listingLimit\" | \"urlStateEnabled\" | \"customTableColumn\" | \"emptyPrompt\" | \"findItems\" | \"createItem\" | \"editItem\" | \"deleteItems\" | \"getDetailViewLink\" | \"getOnClickTitle\" | \"rowItemActions\" | \"contentEditor\" | \"titleColumnName\" | \"withoutPageTemplateWrapper\" | \"createdByEnabled\" | \"recentlyAccessed\"> & { title: string; description?: string | undefined; additionalRightSideActions?: React.ReactNode[] | undefined; children?: React.ReactNode; }" ], "path": "packages/content-management/table_list_view/src/table_list_view.tsx", "deprecated": false, diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index 939f6d5092f0d..9bc69d95bd8ef 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index 31e619f9852bc..b78c1e5f21832 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.devdocs.json b/api_docs/kbn_content_management_table_list_view_table.devdocs.json index da65a4c927242..2cd0fa155534b 100644 --- a/api_docs/kbn_content_management_table_list_view_table.devdocs.json +++ b/api_docs/kbn_content_management_table_list_view_table.devdocs.json @@ -109,7 +109,7 @@ "section": "def-common.UserContentCommonSchema", "text": "UserContentCommonSchema" }, - ">({ tableCaption, entityName, entityNamePlural, initialFilter: initialQuery, headingId, initialPageSize, listingLimit, urlStateEnabled, customTableColumn, emptyPrompt, rowItemActions, findItems, createItem, editItem, deleteItems, getDetailViewLink, getOnClickTitle, id: listingId, contentEditor, titleColumnName, withoutPageTemplateWrapper, onFetchSuccess, refreshListBouncer, setPageDataTestSubject, createdByEnabled, }: ", + ">({ tableCaption, entityName, entityNamePlural, initialFilter: initialQuery, headingId, initialPageSize, listingLimit, urlStateEnabled, customTableColumn, emptyPrompt, rowItemActions, findItems, createItem, editItem, deleteItems, getDetailViewLink, getOnClickTitle, id: listingId, contentEditor, titleColumnName, withoutPageTemplateWrapper, onFetchSuccess, refreshListBouncer, setPageDataTestSubject, createdByEnabled, recentlyAccessed, }: ", { "pluginId": "@kbn/content-management-table-list-view-table", "scope": "public", @@ -833,6 +833,28 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/content-management-table-list-view-table", + "id": "def-public.TableListViewTableProps.recentlyAccessed", + "type": "Object", + "tags": [], + "label": "recentlyAccessed", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/recently-accessed", + "scope": "common", + "docId": "kibKbnRecentlyAccessedPluginApi", + "section": "def-common.RecentlyAccessed", + "text": "RecentlyAccessed" + }, + ", \"get\"> | undefined" + ], + "path": "packages/content-management/table_list_view_table/src/table_list_view_table.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/content-management-table-list-view-table", "id": "def-public.TableListViewTableProps.tableCaption", diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 74be3983e76db..bdc5a7d428729 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 48 | 0 | 32 | 3 | +| 49 | 0 | 33 | 3 | ## Client diff --git a/api_docs/kbn_content_management_user_profiles.mdx b/api_docs/kbn_content_management_user_profiles.mdx index 2b81c3ba9cb2f..726b4e50bbf78 100644 --- a/api_docs/kbn_content_management_user_profiles.mdx +++ b/api_docs/kbn_content_management_user_profiles.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-user-profiles title: "@kbn/content-management-user-profiles" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-user-profiles plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-user-profiles'] --- import kbnContentManagementUserProfilesObj from './kbn_content_management_user_profiles.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 35e16d09a66e9..1a571a6235c67 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index fcde91201ab8d..376acad1aeca5 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 0b42f6c90100e..20c4f81eaba5d 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index f9e3cc6381c29..bca3557232102 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 150ee1257fee8..9c0d4635cb73f 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index 04d6ad1681d20..3176235db7c47 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index b8916d269cc1e..7e294c2b0f0fe 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index da66c9c3b30fa..988172664fa0a 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index f1faad6e0067e..5e75403ef5b0c 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index 7f963bdf82777..a871b9d73f3f3 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 3d95f71017298..50229a02dc606 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 4cf5f768f4995..c4659d860e4f0 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 25549d6384f10..367d7d2181417 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 78b83d613ebeb..cf6fd06bfc153 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 0556f0f0e9f16..9d8f9e0739bd5 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index fec3cff347d84..5c37132b4f79f 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index a98ef6914e73c..3b74753424a7e 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 4c3adbf26bc60..cbdf54801bf0d 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index bb3dc3876d0b4..2e61f3936e5f8 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index b43ac1eb3aaea..91c2525f2b051 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index f495847303fc9..e18d28c3d1d69 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 8a60d6e77ed4d..15df05f10bf00 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.devdocs.json b/api_docs/kbn_core_chrome_browser.devdocs.json index 21ff311b60832..2a76c438cc5ef 100644 --- a/api_docs/kbn_core_chrome_browser.devdocs.json +++ b/api_docs/kbn_core_chrome_browser.devdocs.json @@ -3716,7 +3716,7 @@ "label": "AppDeepLinkId", "description": [], "signature": [ - "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"metrics\" | \"management\" | \"synthetics\" | \"ux\" | \"apm\" | \"logs\" | \"profiling\" | \"dashboards\" | \"observabilityAIAssistant\" | \"home\" | \"canvas\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:singleMetricViewer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:logRateAnalysis\" | \"ml:logPatternAnalysis\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:cross_cluster_replication\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchInferenceEndpoints\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"observability-logs-explorer\" | \"observabilityOnboarding\" | \"slo\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:services\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"profiling:functions\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:coverage-overview\" | \"securitySolutionUI:notes-management\" | \"fleet:settings\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\" | \"fleet:agents\"" + "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"metrics\" | \"management\" | \"synthetics\" | \"ux\" | \"apm\" | \"logs\" | \"profiling\" | \"dashboards\" | \"observabilityAIAssistant\" | \"home\" | \"canvas\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:singleMetricViewer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:logRateAnalysis\" | \"ml:logPatternAnalysis\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:cross_cluster_replication\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"enterpriseSearchRelevance\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchInferenceEndpoints\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"enterpriseSearchRelevance:inferenceEndpoints\" | \"observability-logs-explorer\" | \"observabilityOnboarding\" | \"slo\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:services\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"profiling:functions\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:coverage-overview\" | \"securitySolutionUI:notes-management\" | \"fleet:settings\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\" | \"fleet:agents\"" ], "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", "deprecated": false, diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 1086abae2b215..d2f73aae6c56a 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 62f7b570e4eca..f67dd9ab55dae 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index e1bde0474cae7..4d00542bb07f5 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index e5468ddc4919c..475603132fb38 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 8096f4620c7ce..5a3daa48a0991 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index dda9d9b03975c..a51c29a157dc7 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index efb3d8c9ed2a3..a968a84ebedb8 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 8407c18d9dbcc..be06b7f5c071f 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 17f9c83d7b6d8..a8469ab10bd6c 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 5a4bbd836d77e..ff47ac18f388d 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 459e0abfecb15..8f547d585c521 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index fbb92ae3c4927..0d1f50c631c94 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index db58daa3dc4c2..588882107a59e 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 6341af0909c1f..72c2fd828595f 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index fea078a886534..ad8707b012f6f 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index 9e35638fe8485..beb2ee0960c03 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index c128ca473df8b..8631921d2992d 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 4831dce8dd2a0..c01a5ad288fc7 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 664f1d96ac2d0..05a02d042fe8f 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index f6d9eb3e892a3..34c6ab10fa7b1 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index bee1dcfa6a53e..0714172c5b8de 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index e7d76027875e6..97ca9a1453d76 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 5378b1943c94c..7dd9526228567 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index 0f29505f85d22..5eca94f8a20cb 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index f10aca9ec1995..99a59f4b23f87 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index cf45779818e76..e2e309d7bd0de 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 8b742ef47ea4d..bb23363db38e1 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 22dc6d6fd2e28..33b321249e0cc 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 898bb2ab07218..fc956352f0656 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 785761fd8670f..1ef300a06de87 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index f52bc3b6dd3e0..a33c5c4e26238 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index c86e95d8e7c09..94084a3fe62d4 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index cb8b66a4c5859..e66dbd0069486 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 50c8cd918d317..a927c475362de 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index c642fcbc62241..20cb7e687ce02 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 323642c224ac4..064521832e249 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index 56b67697fabc8..45f97c1d78e79 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index f76ca30b74ea7..39124862a3323 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index d74fa065174a6..95dfc7b0d171a 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 85d753f6ccda0..5ba46583dc125 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 67aff7366d4b5..93983bf356095 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 11abb16b7388e..01ca25c21e898 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index b28bb841aea21..0cad736e16410 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index a7f5197d057e3..5663ea45d0e3e 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 80ba56edce2f9..a66883c9f2ebe 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 5bd4269119990..a03d2453be975 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 7a78f6a7b7732..6f7f444953b0e 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 12b67e02c9cf0..a3d1fa4bc7086 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index f0d1328317f97..6a882bc634e54 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -9638,6 +9638,10 @@ "plugin": "reporting", "path": "x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts" }, + { + "plugin": "searchInferenceEndpoints", + "path": "x-pack/plugins/search_inference_endpoints/server/routes.ts" + }, { "plugin": "serverlessSearch", "path": "x-pack/plugins/serverless_search/server/routes/connectors_routes.ts" @@ -14638,6 +14642,22 @@ "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/kibana_framework_adapter.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_cluster_health/get_cluster_health_route.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_space_health/get_space_health_route.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_events/get_rule_execution_events_route.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts" + }, { "plugin": "osquery", "path": "x-pack/plugins/osquery/server/routes/live_query/get_live_query_details_route.ts" @@ -14710,22 +14730,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_status/get_prebuilt_rules_status_route.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_cluster_health/get_cluster_health_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_space_health/get_space_health_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_events/get_rule_execution_events_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/find_rules/route.ts" @@ -15317,10 +15321,6 @@ "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/kibana_framework_adapter.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts" - }, { "plugin": "osquery", "path": "x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts" @@ -15329,6 +15329,10 @@ "plugin": "osquery", "path": "x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts" @@ -16078,15 +16082,19 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_cluster_health/get_cluster_health_route.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_rule_health/get_rule_health_route.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_space_health/get_space_health_route.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/setup/setup_health_route.ts" }, { "plugin": "osquery", @@ -16106,35 +16114,31 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/review_rule_installation_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_cluster_health/get_cluster_health_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/review_rule_installation_route.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_rule_health/get_rule_health_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/get_space_health/get_space_health_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/setup/setup_health_route.ts" + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts" }, { "plugin": "securitySolution", diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 5596729a19e5b..614c3b9a7b7e2 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 6c60c5f95888d..2d330ecb3f7b3 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 8df92ed2ed404..cfded8d56635f 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index 6550a4170a723..d7a3c9641cc07 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 306b32ec55c1a..b06ad10414620 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index f849d6e5cead0..c7b11d5a18cb9 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index cdddc0eb5e5e4..dd078b381f972 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index b5e51e530e50d..0f1a495092a62 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index de9f97d537e42..aeb737f9486a5 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index bed7da38edbb4..132a752867fed 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index 9976d4b739156..69b7fcfccb430 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 7534a66110496..2f90e1916554e 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index 576be6a048077..20bc6881efb28 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 7d3abe3f0c9dc..84afe340bafaf 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index 0f501a5406456..09f38b1e9e14b 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 677ea84bfa21a..4e327029c6e75 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index d066406eb194e..d913047ca8b58 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index e36cdb66b8bdb..aa313b06ce52e 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 87f35c880d486..8f4f0db0e831b 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 03c8d13082f19..5e3f3f033a40c 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 0ff44ccb2e726..b40391cf960de 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index b5131595599a6..eae30f58c6863 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index a7631cf8c39bc..adb6933ac0b3e 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index b3bc256921f77..bd353e1fbb474 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index c0c3c776bfa33..ed05e8b871e13 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 623d9f05fd146..785347717a7b0 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 07b5c87f8e1a6..6703f92b23c37 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 0af8a380b8986..b36d927b144df 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 8733bac0f473d..2098ce8ef277f 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 34ad00eaeb7a7..13d674307226e 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index ac75d8f1910b9..a537901ed8762 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 906a5258c3415..5708b905aa3c1 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index b1a65946aacee..98308898b1c03 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index f58effd94bdd3..62cf0ec701516 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index d23b118d23003..996f481cda36f 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index cafedc5b681ba..78fdf2ecdf3a7 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 921d52cf93af5..9e5f4ecc16e7f 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index db3657b741db6..04d4ac0d9b488 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index e76953060daa7..6fc8f8d6a7299 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 91ff579000f9e..4ea22f25705aa 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 046427404d626..5a7b6ad659133 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 1da976fb44be4..64ad58587ebfb 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 480c70239ab38..7ad29baf7ae44 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 23c9a3263b297..d8fbd475a2841 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 511c802b6d141..4ce95e2b3defe 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index e9dc751143066..650588837018d 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 57f88ea5c51df..82850711751d7 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 8fe186570a126..74f7284f16963 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 694f4eb097689..f145e5ae7c0cc 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index e5c4b705bd85f..e3f63ec5585af 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index d37ce6bcdc6e1..d66e01dbe82b9 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index a6611bbed76db..8a1b41554edc3 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index eba8f921ec522..168d96ec1f10c 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 8778e778c0fe5..bc7163705ce0f 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 56e8c07c0f0da..2663aabf63307 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 554b28e4d8a6b..14d437efbbeaa 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index db08f52c6122d..8b33b1b5b0c49 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 2dd11ab51188f..4c80ed2214e6a 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 7645e5be4a95a..1d540f8f98d2b 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 5eebde469fa1d..fd6aec4a47d93 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 654bb0d9a30ca..666a471ecf3f7 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index a3b05a0a497fb..0c9a42112fec9 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index 74d85c41a162b..f3e5286b53133 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index f1089272a33c0..8156b79846db7 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index c1cb8444bc83b..f6ff7e00daefa 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index f55fdf09091ff..f13915cbc1a81 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 718c42829ed7e..2372dd9eb3f88 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index 371aaf08307d5..4678c148eae5b 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.devdocs.json b/api_docs/kbn_core_security_server.devdocs.json index fb144f1f8e2d8..ca536ef16c482 100644 --- a/api_docs/kbn_core_security_server.devdocs.json +++ b/api_docs/kbn_core_security_server.devdocs.json @@ -18,94 +18,1708 @@ }, "common": { "classes": [], - "functions": [], + "functions": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.isCreateRestAPIKeyParams", + "type": "Function", + "tags": [], + "label": "isCreateRestAPIKeyParams", + "description": [], + "signature": [ + "(params: any) => boolean" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.isCreateRestAPIKeyParams.$1", + "type": "Any", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditEvent", + "id": "def-common.APIKeys", + "type": "Interface", + "tags": [], + "label": "APIKeys", + "description": [ + "\nInterface for managing API keys in Elasticsearch, including creation,\nvalidation, and invalidation of API keys,\nas well as checking the status of API key features." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.areAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areAPIKeysEnabled", + "description": [ + "\nDetermines if API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.areCrossClusterAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areCrossClusterAPIKeysEnabled", + "description": [ + "\nDetermines if Cross-Cluster API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [ + "\nTries to create an API key for the current user.\n\nReturns newly created API key or `null` if API keys are disabled.\n\nUser needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", createParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + }, + ") => Promise<", + "SecurityCreateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.create.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.create.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "The params to create an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.update", + "type": "Function", + "tags": [], + "label": "update", + "description": [ + "\nAttempts update an API key with the provided 'role_descriptors' and 'metadata'\n\nReturns `updated`, `true` if the update was successful, `false` if there was nothing to update\n\nUser needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", updateParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + }, + ") => Promise<", + "SecurityUpdateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.update.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.update.$2", + "type": "CompoundType", + "tags": [], + "label": "updateParams", + "description": [ + "The params to edit an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.grantAsInternalUser", + "type": "Function", + "tags": [], + "label": "grantAsInternalUser", + "description": [ + "\nTries to grant an API key for the current user." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", createParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.GrantAPIKeyResult", + "text": "GrantAPIKeyResult" + }, + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.grantAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.grantAsInternalUser.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "Create operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.validate", + "type": "Function", + "tags": [], + "label": "validate", + "description": [ + "\nTries to validate an API key." + ], + "signature": [ + "(apiKeyPrams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + }, + ") => Promise" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.validate.$1", + "type": "Object", + "tags": [], + "label": "apiKeyPrams", + "description": [ + "ValidateAPIKeyParams." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.invalidate", + "type": "Function", + "tags": [], + "label": "invalidate", + "description": [ + "\nTries to invalidate an API keys." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", params: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.invalidate.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.invalidate.$2", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate an API keys." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.invalidateAsInternalUser", + "type": "Function", + "tags": [], + "label": "invalidateAsInternalUser", + "description": [ + "\nTries to invalidate the API keys by using the internal user." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeys.invalidateAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate the API keys." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext", + "type": "Interface", + "tags": [], + "label": "APIKeysServiceWithContext", + "description": [ + "\nPublic API Keys service exposed through core context to manage\nAPI keys in Elasticsearch, including creation,\nvalidation, and invalidation of API keys,\nas well as checking the status of API key features." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.areAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areAPIKeysEnabled", + "description": [ + "\nDetermines if API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [ + "\nTries to create an API key for the current user.\n\nReturns newly created API key or `null` if API keys are disabled.\n\nUser needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.\n" + ], + "signature": [ + "(createParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + }, + ") => Promise<", + "SecurityCreateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.create.$1", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "The params to create an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.update", + "type": "Function", + "tags": [], + "label": "update", + "description": [ + "\nAttempts update an API key with the provided 'role_descriptors' and 'metadata'\n\nReturns `updated`, `true` if the update was successful, `false` if there was nothing to update\n\nUser needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys.\n" + ], + "signature": [ + "(updateParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + }, + ") => Promise<", + "SecurityUpdateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.update.$1", + "type": "CompoundType", + "tags": [], + "label": "updateParams", + "description": [ + "The params to edit an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.validate", + "type": "Function", + "tags": [], + "label": "validate", + "description": [ + "\nTries to validate an API key." + ], + "signature": [ + "(apiKeyPrams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + }, + ") => Promise" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.validate.$1", + "type": "Object", + "tags": [], + "label": "apiKeyPrams", + "description": [ + "ValidateAPIKeyParams." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.invalidate", + "type": "Function", + "tags": [], + "label": "invalidate", + "description": [ + "\nTries to invalidate an API keys." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.APIKeysServiceWithContext.invalidate.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate an API keys." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditEvent", + "type": "Interface", + "tags": [], + "label": "AuditEvent", + "description": [ + "\nAudit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html\n\nIf you add additional fields to the schema ensure you update the Kibana Filebeat module:\nhttps://github.com/elastic/beats/tree/master/filebeat/module/kibana\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditEvent", + "text": "AuditEvent" + }, + " extends ", + { + "pluginId": "@kbn/logging", + "scope": "common", + "docId": "kibKbnLoggingPluginApi", + "section": "def-common.LogMeta", + "text": "LogMeta" + } + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditEvent.message", + "type": "string", + "tags": [], + "label": "message", + "description": [ + "\nLog message" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditEvent.kibana", + "type": "Object", + "tags": [], + "label": "kibana", + "description": [ + "\nKibana specific fields" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditKibana", + "text": "AuditKibana" + }, + " | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditEvent.http", + "type": "Object", + "tags": [], + "label": "http", + "description": [ + "\nFields describing an HTTP request" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditHttp", + "text": "AuditHttp" + }, + " | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditHttp", + "type": "Interface", + "tags": [], + "label": "AuditHttp", + "description": [ + "\nAudit http schema using ECS format" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditHttp", + "text": "AuditHttp" + }, + " extends ", + "EcsHttp" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditHttp.request", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "\nHTTP request details" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditRequest", + "text": "AuditRequest" + }, + " | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana", + "type": "Interface", + "tags": [], + "label": "AuditKibana", + "description": [ + "\nAudit kibana schema using ECS format" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.space_id", + "type": "string", + "tags": [], + "label": "space_id", + "description": [ + "\nThe ID of the space associated with this event." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.session_id", + "type": "string", + "tags": [], + "label": "session_id", + "description": [ + "\nThe ID of the user session associated with this event. Each login attempt\nresults in a unique session id." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.saved_object", + "type": "Object", + "tags": [], + "label": "saved_object", + "description": [ + "\nSaved object that was created, changed, deleted or accessed as part of this event." + ], + "signature": [ + "{ type: string; id: string; name?: string | undefined; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.authentication_provider", + "type": "string", + "tags": [], + "label": "authentication_provider", + "description": [ + "\nName of authentication provider associated with a login event." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.authentication_type", + "type": "string", + "tags": [], + "label": "authentication_type", + "description": [ + "\nType of authentication provider associated with a login event." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.authentication_realm", + "type": "string", + "tags": [], + "label": "authentication_realm", + "description": [ + "\nName of Elasticsearch realm that has authenticated the user." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.lookup_realm", + "type": "string", + "tags": [], + "label": "lookup_realm", + "description": [ + "\nName of Elasticsearch realm where the user details were retrieved from." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.add_to_spaces", + "type": "Object", + "tags": [], + "label": "add_to_spaces", + "description": [ + "\nSet of space IDs that a saved object was shared to." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.delete_from_spaces", + "type": "Object", + "tags": [], + "label": "delete_from_spaces", + "description": [ + "\nSet of space IDs that a saved object was removed from." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.unauthorized_spaces", + "type": "Object", + "tags": [], + "label": "unauthorized_spaces", + "description": [ + "\nSet of space IDs that are not authorized for an action." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditKibana.unauthorized_types", + "type": "Object", + "tags": [], + "label": "unauthorized_types", + "description": [ + "\nSet of types that are not authorized for an action." + ], + "signature": [ + "readonly string[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditLogger", + "type": "Interface", + "tags": [], + "label": "AuditLogger", + "description": [], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditLogger.log", + "type": "Function", + "tags": [], + "label": "log", + "description": [ + "\nLogs an {@link AuditEvent} and automatically adds meta data about the\ncurrent user, space and correlation id.\n\nGuidelines around what events should be logged and how they should be\nstructured can be found in: `/x-pack/plugins/security/README.md`\n" + ], + "signature": [ + "(event: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditEvent", + "text": "AuditEvent" + }, + " | undefined) => void" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditLogger.log.$1", + "type": "Object", + "tags": [], + "label": "event", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditEvent", + "text": "AuditEvent" + }, + " | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditLogger.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [ + "\nIndicates whether audit logging is enabled or not.\n\nUseful for skipping resource-intense operations that don't need to be performed when audit\nlogging is disabled." + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditRequest", + "type": "Interface", + "tags": [], + "label": "AuditRequest", + "description": [ + "\nAudit request schema using ECS format" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditRequest", + "text": "AuditRequest" + }, + " extends { body?: { bytes?: number | undefined; content?: string | undefined; } | undefined; bytes?: number | undefined; id?: string | undefined; method?: string | undefined; mime_type?: string | undefined; referrer?: string | undefined; }" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditRequest.headers", + "type": "Object", + "tags": [], + "label": "headers", + "description": [ + "\nHTTP request headers" + ], + "signature": [ + "{ 'x-forwarded-for'?: string | undefined; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "AuditRequestHandlerContext", + "description": [], + "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuditRequestHandlerContext.logger", + "type": "Object", + "tags": [], + "label": "logger", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditLogger", + "text": "AuditLogger" + } + ], + "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuthcRequestHandlerContext", + "type": "Interface", + "tags": [], + "label": "AuthcRequestHandlerContext", + "description": [], + "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuthcRequestHandlerContext.getCurrentUser", + "type": "Function", + "tags": [], + "label": "getCurrentUser", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/core-security-common", + "scope": "common", + "docId": "kibKbnCoreSecurityCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " | null" + ], + "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.AuthcRequestHandlerContext.apiKeys", + "type": "Object", + "tags": [], + "label": "apiKeys", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.APIKeysServiceWithContext", + "text": "APIKeysServiceWithContext" + } + ], + "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuditService", + "type": "Interface", + "tags": [], + "label": "CoreAuditService", + "description": [], + "path": "packages/core/security/core-security-server/src/audit.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuditService.asScoped", + "type": "Function", + "tags": [], + "label": "asScoped", + "description": [ + "\nCreates an {@link AuditLogger} scoped to the current request.\n\nThis audit logger logs events with all required user and session info and should be used for\nall user-initiated actions.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditLogger", + "text": "AuditLogger" + } + ], + "path": "packages/core/security/core-security-server/src/audit.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuditService.asScoped.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/audit.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuditService.withoutRequest", + "type": "Object", + "tags": [], + "label": "withoutRequest", + "description": [ + "\n{@link AuditLogger} for background tasks only.\n\nThis audit logger logs events without any user or session info and should never be used to log\nuser-initiated actions.\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditLogger", + "text": "AuditLogger" + } + ], + "path": "packages/core/security/core-security-server/src/audit.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuthenticationService", + "type": "Interface", + "tags": [], + "label": "CoreAuthenticationService", + "description": [ + "\nCore's authentication service\n" + ], + "path": "packages/core/security/core-security-server/src/authc.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuthenticationService.getCurrentUser", + "type": "Function", + "tags": [], + "label": "getCurrentUser", + "description": [ + "\nRetrieve the user bound to the provided request, or null if\nno user is authenticated.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ") => ", + { + "pluginId": "@kbn/core-security-common", + "scope": "common", + "docId": "kibKbnCoreSecurityCommonPluginApi", + "section": "def-common.AuthenticatedUser", + "text": "AuthenticatedUser" + }, + " | null" + ], + "path": "packages/core/security/core-security-server/src/authc.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuthenticationService.getCurrentUser.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "The request to retrieve the authenticated user for." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authc.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreAuthenticationService.apiKeys", + "type": "Object", + "tags": [], + "label": "apiKeys", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.APIKeys", + "text": "APIKeys" + } + ], + "path": "packages/core/security/core-security-server/src/authc.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreFipsService", + "type": "Interface", + "tags": [], + "label": "CoreFipsService", + "description": [ + "\nCore's FIPS service\n" + ], + "path": "packages/core/security/core-security-server/src/fips.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreFipsService.isEnabled", + "type": "Function", + "tags": [], + "label": "isEnabled", + "description": [ + "\nCheck if Kibana is configured to run in FIPS mode" + ], + "signature": [ + "() => boolean" + ], + "path": "packages/core/security/core-security-server/src/fips.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreSecurityDelegateContract", "type": "Interface", "tags": [], - "label": "AuditEvent", + "label": "CoreSecurityDelegateContract", "description": [ - "\nAudit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html\n\nIf you add additional fields to the schema ensure you update the Kibana Filebeat module:\nhttps://github.com/elastic/beats/tree/master/filebeat/module/kibana\n" + "\nThe contract exposed by the security provider for Core to\nconsume and re-expose via its security service.\n" ], - "signature": [ + "path": "packages/core/security/core-security-server/src/api_provider.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditEvent", - "text": "AuditEvent" + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreSecurityDelegateContract.authc", + "type": "Object", + "tags": [], + "label": "authc", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CoreAuthenticationService", + "text": "CoreAuthenticationService" + } + ], + "path": "packages/core/security/core-security-server/src/api_provider.ts", + "deprecated": false, + "trackAdoption": false }, - " extends ", { - "pluginId": "@kbn/logging", - "scope": "common", - "docId": "kibKbnLoggingPluginApi", - "section": "def-common.LogMeta", - "text": "LogMeta" + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CoreSecurityDelegateContract.audit", + "type": "Object", + "tags": [], + "label": "audit", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CoreAuditService", + "text": "CoreAuditService" + } + ], + "path": "packages/core/security/core-security-server/src/api_provider.ts", + "deprecated": false, + "trackAdoption": false } ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateCrossClusterAPIKeyParams", + "type": "Interface", + "tags": [], + "label": "CreateCrossClusterAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditEvent.message", + "id": "def-common.CreateCrossClusterAPIKeyParams.type", "type": "string", "tags": [], - "label": "message", - "description": [ - "\nLog message" + "label": "type", + "description": [], + "signature": [ + "\"cross_cluster\"" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditEvent.kibana", - "type": "Object", + "id": "def-common.CreateCrossClusterAPIKeyParams.expiration", + "type": "string", "tags": [], - "label": "kibana", - "description": [ - "\nKibana specific fields" - ], + "label": "expiration", + "description": [], "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditKibana", - "text": "AuditKibana" - }, - " | undefined" + "string | undefined" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditEvent.http", + "id": "def-common.CreateCrossClusterAPIKeyParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateCrossClusterAPIKeyParams.metadata", "type": "Object", "tags": [], - "label": "http", - "description": [ - "\nFields describing an HTTP request" + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateCrossClusterAPIKeyParams.access", + "type": "Object", + "tags": [], + "label": "access", + "description": [], "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditHttp", - "text": "AuditHttp" - }, - " | undefined" + "{ search?: { names: string[]; query?: unknown; field_security?: unknown; allow_restricted_indices?: boolean | undefined; }[] | undefined; replication?: { names: string[]; }[] | undefined; }" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -114,48 +1728,176 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditHttp", + "id": "def-common.CreateRestAPIKeyParams", "type": "Interface", "tags": [], - "label": "AuditHttp", - "description": [ - "\nAudit http schema using ECS format" - ], - "signature": [ + "label": "CreateRestAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditHttp", - "text": "AuditHttp" + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false }, - " extends ", - "EcsHttp" + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyParams.role_descriptors", + "type": "Object", + "tags": [], + "label": "role_descriptors", + "description": [], + "signature": [ + "{ [x: string]: { [key: string]: any; }; }" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "type": "Interface", + "tags": [], + "label": "CreateRestAPIKeyWithKibanaPrivilegesParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditHttp.request", + "id": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams.metadata", "type": "Object", "tags": [], - "label": "request", - "description": [ - "\nHTTP request details" + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams.kibana_role_descriptors", + "type": "Object", + "tags": [], + "label": "kibana_role_descriptors", + "description": [], "signature": [ + "{ [x: string]: { elasticsearch: ", { "pluginId": "@kbn/core-security-server", "scope": "common", "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditRequest", - "text": "AuditRequest" + "section": "def-common.ElasticsearchPrivilegesType", + "text": "ElasticsearchPrivilegesType" }, - " | undefined" + " & { [key: string]: unknown; }; kibana: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.KibanaPrivilegesType", + "text": "KibanaPrivilegesType" + }, + "; }; }" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -164,266 +1906,214 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana", + "id": "def-common.ElasticsearchPrivilegesType", "type": "Interface", "tags": [], - "label": "AuditKibana", + "label": "ElasticsearchPrivilegesType", "description": [ - "\nAudit kibana schema using ECS format" + "\nType representing Elasticsearch specific portion of the role definition." ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.space_id", - "type": "string", + "id": "def-common.ElasticsearchPrivilegesType.cluster", + "type": "Array", "tags": [], - "label": "space_id", - "description": [ - "\nThe ID of the space associated with this event." - ], + "label": "cluster", + "description": [], "signature": [ - "string | undefined" + "string[] | undefined" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.session_id", - "type": "string", + "id": "def-common.ElasticsearchPrivilegesType.remote_cluster", + "type": "Array", "tags": [], - "label": "session_id", - "description": [ - "\nThe ID of the user session associated with this event. Each login attempt\nresults in a unique session id." - ], + "label": "remote_cluster", + "description": [], "signature": [ - "string | undefined" + "{ privileges: string[]; clusters: string[]; }[] | undefined" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.saved_object", - "type": "Object", + "id": "def-common.ElasticsearchPrivilegesType.indices", + "type": "Array", "tags": [], - "label": "saved_object", - "description": [ - "\nSaved object that was created, changed, deleted or accessed as part of this event." - ], + "label": "indices", + "description": [], "signature": [ - "{ type: string; id: string; name?: string | undefined; } | undefined" + "{ names: string[]; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; privileges: string[]; query?: string | undefined; allow_restricted_indices?: boolean | undefined; }[] | undefined" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.authentication_provider", - "type": "string", + "id": "def-common.ElasticsearchPrivilegesType.remote_indices", + "type": "Array", "tags": [], - "label": "authentication_provider", - "description": [ - "\nName of authentication provider associated with a login event." - ], + "label": "remote_indices", + "description": [], "signature": [ - "string | undefined" + "{ clusters: string[]; names: string[]; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; privileges: string[]; query?: string | undefined; allow_restricted_indices?: boolean | undefined; }[] | undefined" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.authentication_type", - "type": "string", + "id": "def-common.ElasticsearchPrivilegesType.run_as", + "type": "Array", "tags": [], - "label": "authentication_type", - "description": [ - "\nType of authentication provider associated with a login event." - ], + "label": "run_as", + "description": [], "signature": [ - "string | undefined" + "string[] | undefined" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.GrantAPIKeyResult", + "type": "Interface", + "tags": [], + "label": "GrantAPIKeyResult", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.authentication_realm", + "id": "def-common.GrantAPIKeyResult.id", "type": "string", "tags": [], - "label": "authentication_realm", + "label": "id", "description": [ - "\nName of Elasticsearch realm that has authenticated the user." - ], - "signature": [ - "string | undefined" + "\nUnique id for this API key" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.lookup_realm", + "id": "def-common.GrantAPIKeyResult.name", "type": "string", "tags": [], - "label": "lookup_realm", + "label": "name", "description": [ - "\nName of Elasticsearch realm where the user details were retrieved from." - ], - "signature": [ - "string | undefined" + "\nName for this API key" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.add_to_spaces", - "type": "Object", + "id": "def-common.GrantAPIKeyResult.api_key", + "type": "string", "tags": [], - "label": "add_to_spaces", + "label": "api_key", "description": [ - "\nSet of space IDs that a saved object was shared to." - ], - "signature": [ - "readonly string[] | undefined" + "\nGenerated API key" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.InvalidateAPIKeyResult", + "type": "Interface", + "tags": [], + "label": "InvalidateAPIKeyResult", + "description": [ + "\nThe return value when invalidating an API key in Elasticsearch." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.delete_from_spaces", - "type": "Object", + "id": "def-common.InvalidateAPIKeyResult.invalidated_api_keys", + "type": "Array", "tags": [], - "label": "delete_from_spaces", + "label": "invalidated_api_keys", "description": [ - "\nSet of space IDs that a saved object was removed from." + "\nThe IDs of the API keys that were invalidated as part of the request." ], "signature": [ - "readonly string[] | undefined" + "string[]" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.unauthorized_spaces", - "type": "Object", + "id": "def-common.InvalidateAPIKeyResult.previously_invalidated_api_keys", + "type": "Array", "tags": [], - "label": "unauthorized_spaces", + "label": "previously_invalidated_api_keys", "description": [ - "\nSet of space IDs that are not authorized for an action." + "\nThe IDs of the API keys that were already invalidated." ], "signature": [ - "readonly string[] | undefined" + "string[]" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditKibana.unauthorized_types", - "type": "Object", + "id": "def-common.InvalidateAPIKeyResult.error_count", + "type": "number", "tags": [], - "label": "unauthorized_types", + "label": "error_count", "description": [ - "\nSet of types that are not authorized for an action." + "\nThe number of errors that were encountered when invalidating the API keys." ], - "signature": [ - "readonly string[] | undefined" - ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditLogger", - "type": "Interface", - "tags": [], - "label": "AuditLogger", - "description": [], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditLogger.log", - "type": "Function", + "id": "def-common.InvalidateAPIKeyResult.error_details", + "type": "Array", "tags": [], - "label": "log", + "label": "error_details", "description": [ - "\nLogs an {@link AuditEvent} and automatically adds meta data about the\ncurrent user, space and correlation id.\n\nGuidelines around what events should be logged and how they should be\nstructured can be found in: `/x-pack/plugins/security/README.md`\n" + "\nDetails about these errors. This field is not present in the response when error_count is 0." ], "signature": [ - "(event: ", - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditEvent", - "text": "AuditEvent" - }, - " | undefined) => void" - ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditLogger.log.$1", - "type": "Object", - "tags": [], - "label": "event", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditEvent", - "text": "AuditEvent" - }, - " | undefined" - ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditLogger.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [ - "\nIndicates whether audit logging is enabled or not.\n\nUseful for skipping resource-intense operations that don't need to be performed when audit\nlogging is disabled." + "{ type?: string | undefined; reason?: string | undefined; caused_by?: { type?: string | undefined; reason?: string | undefined; } | undefined; }[] | undefined" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_logger.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -432,40 +2122,30 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditRequest", + "id": "def-common.InvalidateAPIKeysParams", "type": "Interface", "tags": [], - "label": "AuditRequest", + "label": "InvalidateAPIKeysParams", "description": [ - "\nAudit request schema using ECS format" - ], - "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditRequest", - "text": "AuditRequest" - }, - " extends { body?: { bytes?: number | undefined; content?: string | undefined; } | undefined; bytes?: number | undefined; id?: string | undefined; method?: string | undefined; mime_type?: string | undefined; referrer?: string | undefined; }" + "\nRepresents the params for invalidating multiple API keys" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditRequest.headers", - "type": "Object", + "id": "def-common.InvalidateAPIKeysParams.ids", + "type": "Array", "tags": [], - "label": "headers", + "label": "ids", "description": [ - "\nHTTP request headers" + "\nList of unique API key IDs" ], "signature": [ - "{ 'x-forwarded-for'?: string | undefined; } | undefined" + "string[]" ], - "path": "packages/core/security/core-security-server/src/audit_logging/audit_events.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -474,10 +2154,10 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditRequestHandlerContext", + "id": "def-common.SecurityRequestHandlerContext", "type": "Interface", "tags": [], - "label": "AuditRequestHandlerContext", + "label": "SecurityRequestHandlerContext", "description": [], "path": "packages/core/security/core-security-server/src/request_handler_context.ts", "deprecated": false, @@ -485,125 +2165,101 @@ "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuditRequestHandlerContext.logger", + "id": "def-common.SecurityRequestHandlerContext.authc", "type": "Object", "tags": [], - "label": "logger", + "label": "authc", "description": [], "signature": [ { "pluginId": "@kbn/core-security-server", "scope": "common", "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditLogger", - "text": "AuditLogger" + "section": "def-common.AuthcRequestHandlerContext", + "text": "AuthcRequestHandlerContext" } ], "path": "packages/core/security/core-security-server/src/request_handler_context.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuthcRequestHandlerContext", - "type": "Interface", - "tags": [], - "label": "AuthcRequestHandlerContext", - "description": [], - "path": "packages/core/security/core-security-server/src/request_handler_context.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.AuthcRequestHandlerContext.getCurrentUser", - "type": "Function", + "id": "def-common.SecurityRequestHandlerContext.audit", + "type": "Object", "tags": [], - "label": "getCurrentUser", + "label": "audit", "description": [], "signature": [ - "() => ", { - "pluginId": "@kbn/core-security-common", + "pluginId": "@kbn/core-security-server", "scope": "common", - "docId": "kibKbnCoreSecurityCommonPluginApi", - "section": "def-common.AuthenticatedUser", - "text": "AuthenticatedUser" - }, - " | null" + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.AuditRequestHandlerContext", + "text": "AuditRequestHandlerContext" + } ], "path": "packages/core/security/core-security-server/src/request_handler_context.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false } ], "initialIsOpen": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreAuditService", + "id": "def-common.SecurityServiceSetup", "type": "Interface", "tags": [], - "label": "CoreAuditService", - "description": [], - "path": "packages/core/security/core-security-server/src/audit.ts", + "label": "SecurityServiceSetup", + "description": [ + "\nSetup contract for Core's security service.\n" + ], + "path": "packages/core/security/core-security-server/src/contracts.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreAuditService.asScoped", + "id": "def-common.SecurityServiceSetup.registerSecurityDelegate", "type": "Function", "tags": [], - "label": "asScoped", + "label": "registerSecurityDelegate", "description": [ - "\nCreates an {@link AuditLogger} scoped to the current request.\n\nThis audit logger logs events with all required user and session info and should be used for\nall user-initiated actions.\n" + "\nRegister the security implementation that then will be used and re-exposed by Core.\n" ], "signature": [ - "(request: ", - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - ") => ", + "(api: ", { "pluginId": "@kbn/core-security-server", "scope": "common", "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditLogger", - "text": "AuditLogger" - } + "section": "def-common.CoreSecurityDelegateContract", + "text": "CoreSecurityDelegateContract" + }, + ") => void" ], - "path": "packages/core/security/core-security-server/src/audit.ts", + "path": "packages/core/security/core-security-server/src/contracts.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreAuditService.asScoped.$1", + "id": "def-common.SecurityServiceSetup.registerSecurityDelegate.$1", "type": "Object", "tags": [], - "label": "request", + "label": "api", "description": [], "signature": [ { - "pluginId": "@kbn/core-http-server", + "pluginId": "@kbn/core-security-server", "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - "" + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CoreSecurityDelegateContract", + "text": "CoreSecurityDelegateContract" + } ], - "path": "packages/core/security/core-security-server/src/audit.ts", + "path": "packages/core/security/core-security-server/src/contracts.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -613,23 +2269,23 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreAuditService.withoutRequest", + "id": "def-common.SecurityServiceSetup.fips", "type": "Object", "tags": [], - "label": "withoutRequest", + "label": "fips", "description": [ - "\n{@link AuditLogger} for background tasks only.\n\nThis audit logger logs events without any user or session info and should never be used to log\nuser-initiated actions.\n" + "\nThe {@link CoreFipsService | FIPS service}" ], "signature": [ { "pluginId": "@kbn/core-security-server", "scope": "common", "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditLogger", - "text": "AuditLogger" + "section": "def-common.CoreFipsService", + "text": "CoreFipsService" } ], - "path": "packages/core/security/core-security-server/src/audit.ts", + "path": "packages/core/security/core-security-server/src/contracts.ts", "deprecated": false, "trackAdoption": false } @@ -638,163 +2294,220 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreAuthenticationService", + "id": "def-common.SecurityServiceStart", "type": "Interface", "tags": [], - "label": "CoreAuthenticationService", + "label": "SecurityServiceStart", "description": [ - "\nCore's authentication service\n" + "\nStart contract for Core's security service.\n" ], - "path": "packages/core/security/core-security-server/src/authc.ts", + "path": "packages/core/security/core-security-server/src/contracts.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreAuthenticationService.getCurrentUser", - "type": "Function", + "id": "def-common.SecurityServiceStart.authc", + "type": "Object", "tags": [], - "label": "getCurrentUser", + "label": "authc", "description": [ - "\nRetrieve the user bound to the provided request, or null if\nno user is authenticated.\n" + "\nThe {@link CoreAuthenticationService | authentication service}" ], "signature": [ - "(request: ", - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - ") => ", { - "pluginId": "@kbn/core-security-common", + "pluginId": "@kbn/core-security-server", "scope": "common", - "docId": "kibKbnCoreSecurityCommonPluginApi", - "section": "def-common.AuthenticatedUser", - "text": "AuthenticatedUser" - }, - " | null" + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CoreAuthenticationService", + "text": "CoreAuthenticationService" + } ], - "path": "packages/core/security/core-security-server/src/authc.ts", + "path": "packages/core/security/core-security-server/src/contracts.ts", "deprecated": false, - "trackAdoption": false, - "children": [ + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.SecurityServiceStart.audit", + "type": "Object", + "tags": [], + "label": "audit", + "description": [ + "\nThe {@link CoreAuditService | audit service}" + ], + "signature": [ { - "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreAuthenticationService.getCurrentUser.$1", - "type": "Object", - "tags": [], - "label": "request", - "description": [ - "The request to retrieve the authenticated user for." - ], - "signature": [ - { - "pluginId": "@kbn/core-http-server", - "scope": "common", - "docId": "kibKbnCoreHttpServerPluginApi", - "section": "def-common.KibanaRequest", - "text": "KibanaRequest" - }, - "" - ], - "path": "packages/core/security/core-security-server/src/authc.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CoreAuditService", + "text": "CoreAuditService" } ], - "returnComment": [] + "path": "packages/core/security/core-security-server/src/contracts.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreFipsService", + "id": "def-common.UpdateCrossClusterAPIKeyParams", "type": "Interface", "tags": [], - "label": "CoreFipsService", - "description": [ - "\nCore's FIPS service\n" - ], - "path": "packages/core/security/core-security-server/src/fips.ts", + "label": "UpdateCrossClusterAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreFipsService.isEnabled", - "type": "Function", + "id": "def-common.UpdateCrossClusterAPIKeyParams.id", + "type": "string", "tags": [], - "label": "isEnabled", - "description": [ - "\nCheck if Kibana is configured to run in FIPS mode" + "label": "id", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateCrossClusterAPIKeyParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"cross_cluster\"" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateCrossClusterAPIKeyParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateCrossClusterAPIKeyParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], "signature": [ - "() => boolean" + "{ [key: string]: any; } | undefined" ], - "path": "packages/core/security/core-security-server/src/fips.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateCrossClusterAPIKeyParams.access", + "type": "Object", + "tags": [], + "label": "access", + "description": [], + "signature": [ + "{ search?: { names: string[]; query?: unknown; field_security?: unknown; allow_restricted_indices?: boolean | undefined; }[] | undefined; replication?: { names: string[]; }[] | undefined; }" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreSecurityDelegateContract", + "id": "def-common.UpdateRestAPIKeyParams", "type": "Interface", "tags": [], - "label": "CoreSecurityDelegateContract", - "description": [ - "\nThe contract exposed by the security provider for Core to\nconsume and re-expose via its security service.\n" - ], - "path": "packages/core/security/core-security-server/src/api_provider.ts", + "label": "UpdateRestAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreSecurityDelegateContract.authc", + "id": "def-common.UpdateRestAPIKeyParams.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateRestAPIKeyParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateRestAPIKeyParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateRestAPIKeyParams.role_descriptors", "type": "Object", "tags": [], - "label": "authc", + "label": "role_descriptors", "description": [], "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.CoreAuthenticationService", - "text": "CoreAuthenticationService" - } + "{ [x: string]: { [key: string]: unknown; }; }" ], - "path": "packages/core/security/core-security-server/src/api_provider.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.CoreSecurityDelegateContract.audit", + "id": "def-common.UpdateRestAPIKeyParams.metadata", "type": "Object", "tags": [], - "label": "audit", + "label": "metadata", "description": [], "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.CoreAuditService", - "text": "CoreAuditService" - } + "{ [key: string]: any; } | undefined" ], - "path": "packages/core/security/core-security-server/src/api_provider.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -803,138 +2516,95 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityRequestHandlerContext", + "id": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams", "type": "Interface", "tags": [], - "label": "SecurityRequestHandlerContext", + "label": "UpdateRestAPIKeyWithKibanaPrivilegesParams", "description": [], - "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityRequestHandlerContext.authc", - "type": "Object", + "id": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams.id", + "type": "string", "tags": [], - "label": "authc", + "label": "id", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams.type", + "type": "string", + "tags": [], + "label": "type", "description": [], "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuthcRequestHandlerContext", - "text": "AuthcRequestHandlerContext" - } + "\"rest\" | undefined" ], - "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityRequestHandlerContext.audit", - "type": "Object", + "id": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams.expiration", + "type": "string", "tags": [], - "label": "audit", + "label": "expiration", "description": [], "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.AuditRequestHandlerContext", - "text": "AuditRequestHandlerContext" - } + "string | undefined" ], - "path": "packages/core/security/core-security-server/src/request_handler_context.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityServiceSetup", - "type": "Interface", - "tags": [], - "label": "SecurityServiceSetup", - "description": [ - "\nSetup contract for Core's security service.\n" - ], - "path": "packages/core/security/core-security-server/src/contracts.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityServiceSetup.registerSecurityDelegate", - "type": "Function", + "id": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams.metadata", + "type": "Object", "tags": [], - "label": "registerSecurityDelegate", - "description": [ - "\nRegister the security implementation that then will be used and re-exposed by Core.\n" - ], + "label": "metadata", + "description": [], "signature": [ - "(api: ", - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.CoreSecurityDelegateContract", - "text": "CoreSecurityDelegateContract" - }, - ") => void" + "{ [key: string]: any; } | undefined" ], - "path": "packages/core/security/core-security-server/src/contracts.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityServiceSetup.registerSecurityDelegate.$1", - "type": "Object", - "tags": [], - "label": "api", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.CoreSecurityDelegateContract", - "text": "CoreSecurityDelegateContract" - } - ], - "path": "packages/core/security/core-security-server/src/contracts.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityServiceSetup.fips", + "id": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams.kibana_role_descriptors", "type": "Object", "tags": [], - "label": "fips", - "description": [ - "\nThe {@link CoreFipsService | FIPS service}" - ], + "label": "kibana_role_descriptors", + "description": [], "signature": [ + "{ [x: string]: { elasticsearch: ", { "pluginId": "@kbn/core-security-server", "scope": "common", "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.CoreFipsService", - "text": "CoreFipsService" - } + "section": "def-common.ElasticsearchPrivilegesType", + "text": "ElasticsearchPrivilegesType" + }, + " & { [key: string]: unknown; }; kibana: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.KibanaPrivilegesType", + "text": "KibanaPrivilegesType" + }, + "; }; }" ], - "path": "packages/core/security/core-security-server/src/contracts.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -943,58 +2613,40 @@ }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityServiceStart", + "id": "def-common.ValidateAPIKeyParams", "type": "Interface", "tags": [], - "label": "SecurityServiceStart", + "label": "ValidateAPIKeyParams", "description": [ - "\nStart contract for Core's security service.\n" + "\nRepresents the parameters for validating API Key credentials." ], - "path": "packages/core/security/core-security-server/src/contracts.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityServiceStart.authc", - "type": "Object", + "id": "def-common.ValidateAPIKeyParams.id", + "type": "string", "tags": [], - "label": "authc", + "label": "id", "description": [ - "\nThe {@link CoreAuthenticationService | authentication service}" - ], - "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.CoreAuthenticationService", - "text": "CoreAuthenticationService" - } + "\nUnique id for this API key" ], - "path": "packages/core/security/core-security-server/src/contracts.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/core-security-server", - "id": "def-common.SecurityServiceStart.audit", - "type": "Object", + "id": "def-common.ValidateAPIKeyParams.api_key", + "type": "string", "tags": [], - "label": "audit", + "label": "api_key", "description": [ - "\nThe {@link CoreAuditService | audit service}" - ], - "signature": [ - { - "pluginId": "@kbn/core-security-server", - "scope": "common", - "docId": "kibKbnCoreSecurityServerPluginApi", - "section": "def-common.CoreAuditService", - "text": "CoreAuditService" - } + "\nGenerated API Key (secret)" ], - "path": "packages/core/security/core-security-server/src/contracts.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -1045,6 +2697,133 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateAPIKeyParams", + "type": "Type", + "tags": [], + "label": "CreateAPIKeyParams", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateCrossClusterAPIKeyParams", + "text": "CreateCrossClusterAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.CreateAPIKeyResult", + "type": "Type", + "tags": [], + "label": "CreateAPIKeyResult", + "description": [ + "\nResponse of Kibana Create API key endpoint." + ], + "signature": [ + "SecurityCreateApiKeyResponse" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.KibanaPrivilegesType", + "type": "Type", + "tags": [], + "label": "KibanaPrivilegesType", + "description": [ + "\nType representing Kibana specific portion of the role definition." + ], + "signature": [ + "{ spaces: string[]; base?: string[] | undefined; feature?: Record | undefined; }[]" + ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateAPIKeyParams", + "type": "Type", + "tags": [], + "label": "UpdateAPIKeyParams", + "description": [ + "\nRequest body of Kibana Update API key endpoint." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateRestAPIKeyParams", + "text": "UpdateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateCrossClusterAPIKeyParams", + "text": "UpdateCrossClusterAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams", + "text": "UpdateRestAPIKeyWithKibanaPrivilegesParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-security-server", + "id": "def-common.UpdateAPIKeyResult", + "type": "Type", + "tags": [], + "label": "UpdateAPIKeyResult", + "description": [ + "\nResponse of Kibana Update API key endpoint." + ], + "signature": [ + "SecurityUpdateApiKeyResponse" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "objects": [] diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index e76d5d248794e..4139bf1eae324 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; @@ -21,10 +21,13 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 52 | 0 | 16 | 0 | +| 146 | 1 | 63 | 0 | ## Common +### Functions + + ### Interfaces diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index 97e9a6aa5eddb..9c7ad2035ccb2 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.devdocs.json b/api_docs/kbn_core_security_server_mocks.devdocs.json index b9910d935968b..686b62e5af8fc 100644 --- a/api_docs/kbn_core_security_server_mocks.devdocs.json +++ b/api_docs/kbn_core_security_server_mocks.devdocs.json @@ -90,6 +90,44 @@ } ], "objects": [ + { + "parentPluginId": "@kbn/core-security-server-mocks", + "id": "def-common.apiKeysMock", + "type": "Object", + "tags": [], + "label": "apiKeysMock", + "description": [], + "path": "packages/core/security/core-security-server-mocks/src/api_keys.mock.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-security-server-mocks", + "id": "def-common.apiKeysMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => jest.MockedObjectDeep<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.APIKeys", + "text": "APIKeys" + }, + ">" + ], + "path": "packages/core/security/core-security-server-mocks/src/api_keys.mock.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-security-server-mocks", "id": "def-common.auditLoggerMock", diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index 4837bea5d8289..209c495677293 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 13 | 0 | 13 | 2 | +| 15 | 0 | 15 | 2 | ## Common diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index eae8617e6fe54..fb31caf120bb0 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index a9296a333acaf..3565faa7654ca 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index d4dbdd1869237..39250a7ba184f 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 0b37e06fcc38c..ae5a55848a0ae 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 14e0d72778ecc..063abbf6f8ce1 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 042e552861a7a..2a96726373e10 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index 02cb90067ad44..359518c3074b2 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 730e11f40bd82..e6fbc69fc4b33 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index 5c82e09bc6601..f99f8627deeca 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index e5d18270b2904..c34d787a163f2 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 39c90eea55dc2..65001a51b1d16 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index cc711a972530a..4142b15b2d9db 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index f5e13361357f4..b0e6352eb09e9 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index bca627eb63e23..07976f5268389 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 9bd6a2f4e21f8..8403f8135adca 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index e6934d5ef4f0f..dcd06080ded9f 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 37c1136c9968a..383884d29aa2e 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 208c63217e13e..c0a1d101ad8c1 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index d1495c6f87dfe..f4d7730da2efb 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 3eeb3b2230a88..b6445e6ef9a56 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 1cf3e2b8be0c4..158a04ff11bc2 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 77ba5dec8902c..a6833cfb987d6 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 71e9dd73f06e3..f878721b6fe12 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index ab5718fa6d08e..f2e6bc1e5f05d 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index c9e78b7d26df7..fe9361ab89076 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index ca3566253f9cf..3231c90c05c5d 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 05792794b0a52..322cae4dfa77c 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index e56fff27beafd..efe307180e83a 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 027d32961861c..e55bf0b83222d 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index deb44c2a5a93d..ad5a069dc7c95 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 8a62f537cfc8e..bf9468e73fcf9 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index cb81dc195b531..ff3adf35317d2 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index a6de3df2ee8d3..b4d26872b24e0 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 3612cdf26c0ca..9a8c74e3bd723 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index 8bd13f416db1b..2b03ad087184f 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index 2f4fb0a2b850c..d01d7fe32e132 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index fec53bf278c1f..4daf1d3c445ff 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index d757d1cb65f5b..cf52fa6c3a80e 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index db827308451c8..b55dbe1104ec4 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index 10b51c92bdbf6..8776bd84a8a67 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.devdocs.json b/api_docs/kbn_data_view_utils.devdocs.json index bbef9faf79cd5..c1b48a5d1f3b4 100644 --- a/api_docs/kbn_data_view_utils.devdocs.json +++ b/api_docs/kbn_data_view_utils.devdocs.json @@ -19,6 +19,62 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "@kbn/data-view-utils", + "id": "def-common.convertDatatableColumnToDataViewFieldSpec", + "type": "Function", + "tags": [], + "label": "convertDatatableColumnToDataViewFieldSpec", + "description": [ + "\nConvert a datatable column to a DataViewFieldSpec" + ], + "signature": [ + "(column: ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableColumn", + "text": "DatatableColumn" + }, + ") => ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.FieldSpec", + "text": "FieldSpec" + } + ], + "path": "packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/data-view-utils", + "id": "def-common.convertDatatableColumnToDataViewFieldSpec.$1", + "type": "Object", + "tags": [], + "label": "column", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableColumn", + "text": "DatatableColumn" + } + ], + "path": "packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/data-view-utils", "id": "def-common.createRegExpPatternFrom", diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 35fcf27583c48..37a08519c657c 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 5 | 0 | 4 | 0 | +| 7 | 0 | 5 | 0 | ## Common diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index a2149161db461..7f94ca1b6c62f 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 6516a6992e167..690ee52302f83 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index 81a9e07573cf9..d9ff350331076 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 9eda3599e2dfc..01616f8aa7939 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index ed558c6f415c3..9f109f9ac8e27 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index 371a0d04a5484..c7b45bac65b78 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.devdocs.json b/api_docs/kbn_deeplinks_observability.devdocs.json index 2a2d36cdb7282..3b151a497e3c5 100644 --- a/api_docs/kbn_deeplinks_observability.devdocs.json +++ b/api_docs/kbn_deeplinks_observability.devdocs.json @@ -22,18 +22,18 @@ "interfaces": [ { "parentPluginId": "@kbn/deeplinks-observability", - "id": "def-common.DatasetQualityLocatorParams", + "id": "def-common.DataQualityLocatorParams", "type": "Interface", "tags": [], - "label": "DatasetQualityLocatorParams", + "label": "DataQualityLocatorParams", "description": [], "signature": [ { "pluginId": "@kbn/deeplinks-observability", "scope": "common", "docId": "kibKbnDeeplinksObservabilityPluginApi", - "section": "def-common.DatasetQualityLocatorParams", - "text": "DatasetQualityLocatorParams" + "section": "def-common.DataQualityLocatorParams", + "text": "DataQualityLocatorParams" }, " extends ", { @@ -50,7 +50,7 @@ "children": [ { "parentPluginId": "@kbn/deeplinks-observability", - "id": "def-common.DatasetQualityLocatorParams.filters", + "id": "def-common.DataQualityLocatorParams.filters", "type": "Object", "tags": [], "label": "filters", @@ -61,6 +61,20 @@ "path": "packages/deeplinks/observability/locators/dataset_quality.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/deeplinks-observability", + "id": "def-common.DataQualityLocatorParams.flyout", + "type": "Object", + "tags": [], + "label": "flyout", + "description": [], + "signature": [ + "{ dataset: DatasetConfig; } | undefined" + ], + "path": "packages/deeplinks/observability/locators/dataset_quality.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -754,13 +768,13 @@ }, { "parentPluginId": "@kbn/deeplinks-observability", - "id": "def-common.DATASET_QUALITY_LOCATOR_ID", + "id": "def-common.DATA_QUALITY_LOCATOR_ID", "type": "string", "tags": [], - "label": "DATASET_QUALITY_LOCATOR_ID", + "label": "DATA_QUALITY_LOCATOR_ID", "description": [], "signature": [ - "\"DATASET_QUALITY_LOCATOR\"" + "\"DATA_QUALITY_LOCATOR\"" ], "path": "packages/deeplinks/observability/locators/dataset_quality.ts", "deprecated": false, diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index 86d5dd2c3efb0..a9b981ef63b99 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 55 | 0 | 43 | 0 | +| 56 | 0 | 44 | 0 | ## Common diff --git a/api_docs/kbn_deeplinks_search.devdocs.json b/api_docs/kbn_deeplinks_search.devdocs.json index 312aad5a35d80..d3659c3bf083c 100644 --- a/api_docs/kbn_deeplinks_search.devdocs.json +++ b/api_docs/kbn_deeplinks_search.devdocs.json @@ -30,7 +30,7 @@ "label": "DeepLinkId", "description": [], "signature": [ - "\"appSearch\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchInferenceEndpoints\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\"" + "\"appSearch\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"enterpriseSearchRelevance\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchInferenceEndpoints\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"enterpriseSearchRelevance:inferenceEndpoints\"" ], "path": "packages/deeplinks/search/deep_links.ts", "deprecated": false, @@ -114,13 +114,13 @@ }, { "parentPluginId": "@kbn/deeplinks-search", - "id": "def-common.ENTERPRISE_SEARCH_INFERENCE_ENDPOINTS_APP_ID", + "id": "def-common.ENTERPRISE_SEARCH_RELEVANCE_APP_ID", "type": "string", "tags": [], - "label": "ENTERPRISE_SEARCH_INFERENCE_ENDPOINTS_APP_ID", + "label": "ENTERPRISE_SEARCH_RELEVANCE_APP_ID", "description": [], "signature": [ - "\"enterpriseSearchInferenceEndpoints\"" + "\"enterpriseSearchRelevance\"" ], "path": "packages/deeplinks/search/constants.ts", "deprecated": false, diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 5006993ab7bf5..7632d8c3d8574 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index 230253c75efe9..53f462a5820e4 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index 8754e80b0c295..45f26f34c0bc8 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index df1ab900aef16..077cf495c42a0 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index 45a3b76ab4b57..9b9c2ec67217a 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index dad78c414e3ea..01e11369c69c6 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 96f57d83edca9..314ad13865b67 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index b962fe962b01a..53fd62dbd5207 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 54f5461df533c..adc46e7bed4bf 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 030ee2f59ebe2..80dc1d84a3bcb 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 3b7a727845284..6f8c66c500644 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index 937235a9945c2..1013d41512685 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 079158c906b48..817bd3ade599a 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index e8eedc9b350d8..a6e7f866d5c2a 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 538c23bdbfc3a..992cd44203b59 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt.mdx b/api_docs/kbn_ebt.mdx index 49c57c623e9df..e3cc19e3f4e11 100644 --- a/api_docs/kbn_ebt.mdx +++ b/api_docs/kbn_ebt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt title: "@kbn/ebt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt'] --- import kbnEbtObj from './kbn_ebt.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 955875e54964e..3e947e8b7de85 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 0d562c897bd3f..422beff42fcd3 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 4bf65e3122f2c..2f88ec0fca74b 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index f9fb2031ec976..036f02873ccbf 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index f6184753a263a..283289cb1b986 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.devdocs.json b/api_docs/kbn_entities_schema.devdocs.json index cf8d585a18f80..bcb6b2ff87c2b 100644 --- a/api_docs/kbn_entities_schema.devdocs.json +++ b/api_docs/kbn_entities_schema.devdocs.json @@ -43,7 +43,7 @@ "label": "EntityDefinition", "description": [], "signature": [ - "{ id: string; type: string; name: string; history: { interval: moment.Duration; timestampField: string; lookbackPeriod?: moment.Duration | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }; managed: boolean; indexPatterns: string[]; identityFields: ({ field: string; optional: boolean; } | { field: string; optional: boolean; })[]; displayNameTemplate: string; description?: string | undefined; filter?: string | undefined; metadata?: ({ source: string; destination?: string | undefined; limit?: number | undefined; } | { source: string; destination: string; limit: number; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", + "{ id: string; type: string; version: string; name: string; history: { interval: moment.Duration; timestampField: string; lookbackPeriod?: moment.Duration | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }; managed: boolean; indexPatterns: string[]; identityFields: ({ field: string; optional: boolean; } | { field: string; optional: boolean; })[]; displayNameTemplate: string; description?: string | undefined; filter?: string | undefined; metadata?: ({ source: string; destination?: string | undefined; limit?: number | undefined; } | { source: string; destination: string; limit: number; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", { "pluginId": "@kbn/entities-schema", "scope": "common", @@ -221,7 +221,7 @@ "label": "entityDefinitionSchema", "description": [], "signature": [ - "Zod.ZodObject<{ id: Zod.ZodString; name: Zod.ZodString; description: Zod.ZodOptional; type: Zod.ZodString; filter: Zod.ZodOptional; indexPatterns: Zod.ZodArray; identityFields: Zod.ZodArray, Zod.ZodEffects]>, \"many\">; displayNameTemplate: Zod.ZodString; metadata: Zod.ZodOptional; limit: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { source: string; destination?: string | undefined; limit?: number | undefined; }, { source: string; destination?: string | undefined; limit?: number | undefined; }>, Zod.ZodEffects]>, \"many\">>; metrics: Zod.ZodOptional; name: Zod.ZodString; description: Zod.ZodOptional; type: Zod.ZodString; filter: Zod.ZodOptional; indexPatterns: Zod.ZodArray; identityFields: Zod.ZodArray, Zod.ZodEffects]>, \"many\">; displayNameTemplate: Zod.ZodString; metadata: Zod.ZodOptional; limit: Zod.ZodOptional>; }, \"strip\", Zod.ZodTypeAny, { source: string; destination?: string | undefined; limit?: number | undefined; }, { source: string; destination?: string | undefined; limit?: number | undefined; }>, Zod.ZodEffects]>, \"many\">>; metrics: Zod.ZodOptional, \"many\">>; staticFields: Zod.ZodOptional>; managed: Zod.ZodDefault>; history: Zod.ZodObject<{ timestampField: Zod.ZodString; interval: Zod.ZodEffects, moment.Duration, string>; lookbackPeriod: Zod.ZodOptional>; settings: Zod.ZodOptional; syncDelay: Zod.ZodOptional; frequency: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { interval: moment.Duration; timestampField: string; lookbackPeriod?: moment.Duration | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }, { interval: string; timestampField: string; lookbackPeriod?: string | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }>; latest: Zod.ZodOptional; syncDelay: Zod.ZodOptional; frequency: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }, { settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { id: string; type: string; name: string; history: { interval: moment.Duration; timestampField: string; lookbackPeriod?: moment.Duration | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }; managed: boolean; indexPatterns: string[]; identityFields: ({ field: string; optional: boolean; } | { field: string; optional: boolean; })[]; displayNameTemplate: string; description?: string | undefined; filter?: string | undefined; metadata?: ({ source: string; destination?: string | undefined; limit?: number | undefined; } | { source: string; destination: string; limit: number; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }>, \"many\">>; staticFields: Zod.ZodOptional>; managed: Zod.ZodDefault>; history: Zod.ZodObject<{ timestampField: Zod.ZodString; interval: Zod.ZodEffects, moment.Duration, string>; lookbackPeriod: Zod.ZodOptional>; settings: Zod.ZodOptional; syncDelay: Zod.ZodOptional; frequency: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { interval: moment.Duration; timestampField: string; lookbackPeriod?: moment.Duration | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }, { interval: string; timestampField: string; lookbackPeriod?: string | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }>; latest: Zod.ZodOptional; syncDelay: Zod.ZodOptional; frequency: Zod.ZodOptional; }, \"strip\", Zod.ZodTypeAny, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }, { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }, { settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }>>; }, \"strip\", Zod.ZodTypeAny, { id: string; type: string; version: string; name: string; history: { interval: moment.Duration; timestampField: string; lookbackPeriod?: moment.Duration | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }; managed: boolean; indexPatterns: string[]; identityFields: ({ field: string; optional: boolean; } | { field: string; optional: boolean; })[]; displayNameTemplate: string; description?: string | undefined; filter?: string | undefined; metadata?: ({ source: string; destination?: string | undefined; limit?: number | undefined; } | { source: string; destination: string; limit: number; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", { "pluginId": "@kbn/entities-schema", "scope": "common", @@ -269,7 +269,7 @@ "section": "def-common.BasicAggregations", "text": "BasicAggregations" }, - "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }[] | undefined; staticFields?: Record | undefined; latest?: { settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; } | undefined; }, { id: string; type: string; name: string; history: { interval: string; timestampField: string; lookbackPeriod?: string | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }; indexPatterns: string[]; identityFields: (string | { field: string; optional: boolean; })[]; displayNameTemplate: string; description?: string | undefined; filter?: string | undefined; metadata?: (string | { source: string; destination?: string | undefined; limit?: number | undefined; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", + "; filter?: string | undefined; } | { name: string; aggregation: \"doc_count\"; filter?: string | undefined; } | { name: string; field: string; percentile: number; aggregation: \"percentile\"; filter?: string | undefined; })[]; equation: string; }[] | undefined; staticFields?: Record | undefined; latest?: { settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; } | undefined; }, { id: string; type: string; version: string; name: string; history: { interval: string; timestampField: string; lookbackPeriod?: string | undefined; settings?: { syncField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }; indexPatterns: string[]; identityFields: (string | { field: string; optional: boolean; })[]; displayNameTemplate: string; description?: string | undefined; filter?: string | undefined; metadata?: (string | { source: string; destination?: string | undefined; limit?: number | undefined; })[] | undefined; metrics?: { name: string; metrics: ({ name: string; field: string; aggregation: ", { "pluginId": "@kbn/entities-schema", "scope": "common", @@ -467,6 +467,21 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/entities-schema", + "id": "def-common.semVerSchema", + "type": "Object", + "tags": [], + "label": "semVerSchema", + "description": [], + "signature": [ + "Zod.ZodEffects" + ], + "path": "x-pack/packages/kbn-entities-schema/src/schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ] } diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index 03a08d0d54cf2..36eb8b77f0736 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 18 | 0 | 18 | 0 | +| 19 | 0 | 19 | 0 | ## Common diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 7cf1b70304a8f..ac0a3e9ec363e 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 58d019d0bf8fa..d5998673d4ca5 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index acfaba698e7fa..e6942f63addd1 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 2a985320900f7..126af776fe859 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 82d8789b63717..e7a83316f6d4a 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index ae03b6127e3d0..adfe573c8adcc 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.devdocs.json b/api_docs/kbn_esql_ast.devdocs.json index ba60e35d615c3..9811a14a8fa62 100644 --- a/api_docs/kbn_esql_ast.devdocs.json +++ b/api_docs/kbn_esql_ast.devdocs.json @@ -341,7 +341,13 @@ "text": "ESQLAstNode" }, "[]) => ", - "ESQLParamLiteral", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, "[]" ], "path": "packages/kbn-esql-ast/src/walker/walker.ts", @@ -1508,6 +1514,88 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.ESQLParamLiteral", + "type": "Interface", + "tags": [], + "label": "ESQLParamLiteral", + "description": [], + "signature": [ + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, + " extends ", + "ESQLAstBaseItem", + "" + ], + "path": "packages/kbn-esql-ast/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.ESQLParamLiteral.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"literal\"" + ], + "path": "packages/kbn-esql-ast/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.ESQLParamLiteral.literalType", + "type": "string", + "tags": [], + "label": "literalType", + "description": [], + "signature": [ + "\"param\"" + ], + "path": "packages/kbn-esql-ast/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.ESQLParamLiteral.paramType", + "type": "Uncategorized", + "tags": [], + "label": "paramType", + "description": [], + "signature": [ + "ParamType" + ], + "path": "packages/kbn-esql-ast/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.ESQLParamLiteral.value", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "string | number" + ], + "path": "packages/kbn-esql-ast/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/esql-ast", "id": "def-common.ESQLSource", @@ -2015,7 +2103,13 @@ " | ", "ESQLStringLiteral", " | ", - "ESQLParamLiteral", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, "" ], "path": "packages/kbn-esql-ast/src/types.ts", diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index 409e43359b6b8..6bf2b5c4ea9d9 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 99 | 1 | 96 | 11 | +| 104 | 1 | 101 | 10 | ## Common diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 0aac396b0a5e5..13ea5e35682d5 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.devdocs.json b/api_docs/kbn_esql_validation_autocomplete.devdocs.json index 79100a05ac285..56830eaa4f756 100644 --- a/api_docs/kbn_esql_validation_autocomplete.devdocs.json +++ b/api_docs/kbn_esql_validation_autocomplete.devdocs.json @@ -628,7 +628,13 @@ " | ", "ESQLStringLiteral", " | ", - "ESQLParamLiteral", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, " | ", { "pluginId": "@kbn/esql-ast", @@ -740,7 +746,13 @@ " | ", "ESQLStringLiteral", " | ", - "ESQLParamLiteral", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, " | ", { "pluginId": "@kbn/esql-ast", @@ -836,7 +848,13 @@ " | ", "ESQLStringLiteral", " | ", - "ESQLParamLiteral", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, " | ", { "pluginId": "@kbn/esql-ast", @@ -940,7 +958,13 @@ " | ", "ESQLStringLiteral", " | ", - "ESQLParamLiteral", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLParamLiteral", + "text": "ESQLParamLiteral" + }, " | ", { "pluginId": "@kbn/esql-ast", diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 54d5544dfae3a..d255b890eab4f 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 90452a569323b..27926b3fe96e7 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index 022224ae64082..55b7e6c390d51 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index dffd8f52dd155..5e2858640bb2b 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index dc400e31bde16..e0ddef62ed19a 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index 23af3158f039a..4d1d0c518eb9e 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 7ab020263de7e..cffc605081fb0 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index 197ec0ddd43d8..75cc32464a9f7 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index e56080047a545..31550c0a7938a 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index fdb06c43b4393..47ea9dbbba60d 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 48b9532c8c240..f8e68b2de7323 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index b0282d6e6fe6b..3ae9dc5c41a5f 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 5c4d3964ae8aa..52db674e1c1e2 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_grouping.mdx b/api_docs/kbn_grouping.mdx index 945768bb9e590..3bcd5481373e6 100644 --- a/api_docs/kbn_grouping.mdx +++ b/api_docs/kbn_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grouping title: "@kbn/grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grouping plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grouping'] --- import kbnGroupingObj from './kbn_grouping.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 3ddd9004d5bd9..978f32edffee7 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 9a04a6ac2d41a..5f8a30051949f 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index bd0f10e3f2f7a..07b644cf29279 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 20b2eae2f7b3a..4ddb5f39a167c 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index f8905056b6964..59c0fa084bb94 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 3d1c353f16c2d..983940f971052 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index e26e50409e91a..0b0634528213b 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index cc84a9d96eff2..db2d1f018e895 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 5ffa24e29075d..79d23033a0261 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management.mdx b/api_docs/kbn_index_management.mdx index 67ce5e8cc6dfe..5d986a50981ee 100644 --- a/api_docs/kbn_index_management.mdx +++ b/api_docs/kbn_index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management title: "@kbn/index-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management'] --- import kbnIndexManagementObj from './kbn_index_management.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index 9482370f2098e..b32bb5f046cb7 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 5942980525cf1..94160bdf5b5ec 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 9f75bdc13c9cc..bcb7b29b795dc 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 8b86cea224f66..08950ff96575b 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index 7191c53657e0c..c2c9775d6e5ec 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 885a833b848e8..fd83386622f4d 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 06baa35ff2d4b..d4e627ee9dd44 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index c4bf0298cab27..af2d71e1aee9a 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_json_schemas.mdx b/api_docs/kbn_json_schemas.mdx index d17b72c5d78a3..7bcfb8ea1bbe4 100644 --- a/api_docs/kbn_json_schemas.mdx +++ b/api_docs/kbn_json_schemas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-schemas title: "@kbn/json-schemas" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-schemas plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-schemas'] --- import kbnJsonSchemasObj from './kbn_json_schemas.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 7b7504802df25..7909130f970f4 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index ac6609227b665..0b955032e9fbf 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index 850a3c590f8fb..1eb0fcf84a30b 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index 5ec041d5c749c..3e173b1648910 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index d3a88fe215040..9b37a6f3f3321 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 9c41b224c50fe..aa4f4e7e18a6f 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index b89de140a27b3..c6a4aad1d15b5 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 7a27ffffde80c..904ea66bf2050 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 18176ee9ca602..f8a1f81958bca 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index b4b296032e909..25d4b9eb75a3e 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index a6c83fb1bc8f4..7c5cd5e5013d2 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index ce2df8e5b25cb..407cd6aafef3c 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index ac523aa0416dd..3e8320b04b23a 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index 8a64499c8321e..215fbb0415161 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index bfed7b76b6060..5b1b2807ab339 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 784348ccc434a..b9bf6cf2a7005 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 087ad1b156eca..0819d0bfbb7c8 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index 04e967817f5be..d7c65c76d482d 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index 0b299726b544d..cdf448aa90e66 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index 78c19265dc11f..cd588c4f68580 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 97d381b8e263c..ca2e7179f046a 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index b3383c6fc054b..a3ad4176a8a81 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index ff022fd4098b4..8ee3938cb02c0 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index 61537f4a1be4c..d07f30cc90659 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 1006c08221c4b..2c2828919b7b8 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index f9c0a41562641..8b302035202b3 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index 7244fc9f1399f..e4d590eed7f62 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 55b9791fa2733..093530d03119f 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 65980b4809fb9..a3d030058419c 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 02491f27034d1..9cac3bf2ef225 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index 45eff4760ee26..436156f2347f8 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index ab69959755eca..3db201316a013 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 97f5b22c20e03..9b4b68d42407c 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 0aa6a9294274c..cd216d15d16f0 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 5bc58b4ab29e7..ea033faa0200f 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 30078356d43ff..80762c216bf6c 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index c2fab682158a4..bd8d0d600928d 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index 0e71ba311f3cc..1082fde885fc4 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index 835039597aa7f..ed61af11d2d1a 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 0d109f34bc4f5..18a95ece0efc5 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index efad24db23069..4df647011e94d 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index c1fece43923f3..e96069e4faf31 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index 74644502aff39..9d726ccce7d64 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 97462d503b78b..b4cea76738fd9 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 1c39672f5e2df..994f6b4aae2eb 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index acf4af07efdb0..672547666a301 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index cb29649614e39..88278dbc812c8 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 6b6e2fe6d34fd..07ae4c27e45aa 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 88ac70409c110..638c50fdb137f 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index cb50c19d04a9a..85062f7b2a077 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index dee6a9dcb4281..bc6e8573c93e8 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index c405579e9f465..01805fd74a6ff 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 9924c95c9a34e..4af6c43f357c5 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index 2014c95c4c348..0516477727aba 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index 61e6f419b90f8..84247ae5afaff 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 422af3b6140e3..0d2ac208b7e6f 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 722df1697e829..545592c9554b1 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index 7dbae17bcf545..c5fefaebc736f 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 19c3bd2cc9ed4..c60dba038b7cd 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index c9725297c9842..f133e2beda4cc 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 0a9ec50d257a4..ca1145ef455dc 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 87ce43cac874d..6c54037c12a98 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 696f801a977e9..99c83018e5239 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index b142fbb353f28..de815490846ac 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index e0f7bb5acdade..c1391988264a9 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 72b2179b51701..9a6a8ae21d312 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 4842e6a2b5661..09e6baea4b78c 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 951256c39e536..07d27c59202c7 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 71f7a6297be1a..7b49a30a3d724 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 9d129916ceec2..46c0819bf7568 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index d43937348fb60..739a94c2e424a 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 88d9bf660bd00..3326520edc59e 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index d435ea6427be6..31606dac7f3c0 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 63a01de311743..7113b40106640 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index 65978f536556a..868356cefda9e 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index b7631257ad219..0c1a70db3e0bd 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_recently_accessed.devdocs.json b/api_docs/kbn_recently_accessed.devdocs.json new file mode 100644 index 0000000000000..972588291edf7 --- /dev/null +++ b/api_docs/kbn_recently_accessed.devdocs.json @@ -0,0 +1,269 @@ +{ + "id": "@kbn/recently-accessed", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessedService", + "type": "Class", + "tags": [], + "label": "RecentlyAccessedService", + "description": [], + "path": "packages/kbn-recently-accessed/src/recently_accessed_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessedService.start", + "type": "Function", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "({ http, key }: StartDeps) => ", + { + "pluginId": "@kbn/recently-accessed", + "scope": "common", + "docId": "kibKbnRecentlyAccessedPluginApi", + "section": "def-common.RecentlyAccessed", + "text": "RecentlyAccessed" + } + ], + "path": "packages/kbn-recently-accessed/src/recently_accessed_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessedService.start.$1", + "type": "Object", + "tags": [], + "label": "{ http, key }", + "description": [], + "signature": [ + "StartDeps" + ], + "path": "packages/kbn-recently-accessed/src/recently_accessed_service.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessed", + "type": "Interface", + "tags": [], + "label": "RecentlyAccessed", + "description": [ + "\n{@link RecentlyAccessed | APIs} for recently accessed history." + ], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessed.add", + "type": "Function", + "tags": [], + "label": "add", + "description": [ + "\nAdds a new item to the recently accessed history.\n" + ], + "signature": [ + "(link: string, label: string, id: string) => void" + ], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessed.add.$1", + "type": "string", + "tags": [], + "label": "link", + "description": [ + "a relative URL to the resource (not including the {@link HttpStart.basePath | `http.basePath`})" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessed.add.$2", + "type": "string", + "tags": [], + "label": "label", + "description": [ + "the label to display in the UI" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessed.add.$3", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "a unique string used to de-duplicate the recently accessed list." + ], + "signature": [ + "string" + ], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessed.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [ + "\nGets an Array of the current recently accessed history.\n" + ], + "signature": [ + "() => ", + { + "pluginId": "@kbn/recently-accessed", + "scope": "common", + "docId": "kibKbnRecentlyAccessedPluginApi", + "section": "def-common.RecentlyAccessedHistoryItem", + "text": "RecentlyAccessedHistoryItem" + }, + "[]" + ], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessed.get$", + "type": "Function", + "tags": [], + "label": "get$", + "description": [ + "\nGets an Observable of the array of recently accessed history.\n" + ], + "signature": [ + "() => ", + "Observable", + "<", + { + "pluginId": "@kbn/recently-accessed", + "scope": "common", + "docId": "kibKbnRecentlyAccessedPluginApi", + "section": "def-common.RecentlyAccessedHistoryItem", + "text": "RecentlyAccessedHistoryItem" + }, + "[]>" + ], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessedHistoryItem", + "type": "Interface", + "tags": [], + "label": "RecentlyAccessedHistoryItem", + "description": [], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessedHistoryItem.link", + "type": "string", + "tags": [], + "label": "link", + "description": [], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessedHistoryItem.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/recently-accessed", + "id": "def-common.RecentlyAccessedHistoryItem.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-recently-accessed/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_recently_accessed.mdx b/api_docs/kbn_recently_accessed.mdx new file mode 100644 index 0000000000000..21964aa57344b --- /dev/null +++ b/api_docs/kbn_recently_accessed.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnRecentlyAccessedPluginApi +slug: /kibana-dev-docs/api/kbn-recently-accessed +title: "@kbn/recently-accessed" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/recently-accessed plugin +date: 2024-07-11 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/recently-accessed'] +--- +import kbnRecentlyAccessedObj from './kbn_recently_accessed.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 14 | 0 | 7 | 0 | + +## Common + +### Classes + + +### Interfaces + + diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index f1fa3104d03fa..eb7ab9f741927 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index f4cdffdcac021..d1085688553ef 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index caa046bede436..1a0d8707d891f 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index c0ae43523df83..0c17e23fa2c11 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index eb7e0c948d4b2..da8a398b36802 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index d099fee886f5c..1480f6658b029 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index ecdc0f512a625..5b1446f671584 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 2e185bd3e9c09..164f7409fdf07 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index 77a9baff0a4e7..9ec8ece7caf4a 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index f03e1164abca5..6661bc6008aaa 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index d668328d20e23..3a0ec5768f52b 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index 8ce4258543b46..0af88df1fc84b 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index 8712180c101d5..d49952a3ec283 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index f52846975e606..7e1216c8367c6 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index 6f7f12a59850f..dca1b1a9269cc 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index be41eba0c6a6c..fec364f156d12 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_response_ops_feature_flag_service.mdx b/api_docs/kbn_response_ops_feature_flag_service.mdx index b2289667ccc4e..3ee0b47d3fe68 100644 --- a/api_docs/kbn_response_ops_feature_flag_service.mdx +++ b/api_docs/kbn_response_ops_feature_flag_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-feature-flag-service title: "@kbn/response-ops-feature-flag-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-feature-flag-service plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-feature-flag-service'] --- import kbnResponseOpsFeatureFlagServiceObj from './kbn_response_ops_feature_flag_service.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 3c983f8067808..d659bb58baa7e 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rollup.mdx b/api_docs/kbn_rollup.mdx index 99a5af6503be7..988491b2a2274 100644 --- a/api_docs/kbn_rollup.mdx +++ b/api_docs/kbn_rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rollup title: "@kbn/rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rollup plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rollup'] --- import kbnRollupObj from './kbn_rollup.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index 4962e236dd470..94444ebbcb5d2 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 9361a07a5911f..9ccba988fe0bc 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 54d0ccd633422..6a5734a591024 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 77115422dbec4..4d54c20885721 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index d241357c284f9..07fb03fc1dfb5 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index de2ea6e888589..741c6454818d0 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index e2e90e6248dea..7c330dfcd2ecb 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 5009b6f1dd922..9f80b84162264 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index 7a39ecf13f580..31a11cc8b570d 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index cd01e2a84fc1d..c4fe5de6f8813 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 51cb27c0dced7..507efef4c33fc 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_api_key_management.devdocs.json b/api_docs/kbn_security_api_key_management.devdocs.json index 5fd86dcf3f8c8..661a3acf9b5c9 100644 --- a/api_docs/kbn_security_api_key_management.devdocs.json +++ b/api_docs/kbn_security_api_key_management.devdocs.json @@ -186,10 +186,10 @@ "signature": [ "(apiKey: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CreateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", "text": "CreateAPIKeyParams" }, ") => Promise<", @@ -209,10 +209,10 @@ "description": [], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CreateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", "text": "CreateAPIKeyParams" } ], @@ -234,10 +234,10 @@ "signature": [ "(apiKey: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.UpdateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", "text": "UpdateAPIKeyParams" }, ") => Promise<", @@ -257,10 +257,10 @@ "description": [], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.UpdateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", "text": "UpdateAPIKeyParams" } ], @@ -532,10 +532,10 @@ }, ") => ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CreateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", "text": "CreateAPIKeyParams" } ], @@ -586,10 +586,10 @@ }, ") => ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.UpdateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", "text": "UpdateAPIKeyParams" } ], @@ -976,7 +976,7 @@ "label": "sort", "description": [], "signature": [ - "{ field: \"id\" | \"type\" | \"name\" | \"username\" | \"profile_uid\" | \"metadata\" | \"expired\" | \"creation\" | \"expiration\" | \"role_descriptors\" | \"realm\" | \"invalidated\" | \"realm_type\" | \"limited_by\" | \"_sort\"; direction: \"asc\" | \"desc\"; }" + "{ field: \"id\" | \"type\" | \"name\" | \"username\" | \"profile_uid\" | \"metadata\" | \"expired\" | \"creation\" | \"realm\" | \"role_descriptors\" | \"expiration\" | \"invalidated\" | \"realm_type\" | \"limited_by\" | \"_sort\"; direction: \"asc\" | \"desc\"; }" ], "path": "x-pack/packages/security/api_key_management/src/components/api_keys_api_client.ts", "deprecated": false, @@ -1131,9 +1131,31 @@ "label": "CreateAPIKeyParams", "description": [], "signature": [ - "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }> | Readonly<{ metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { type: \"cross_cluster\"; name: string; access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }>" + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateCrossClusterAPIKeyParams", + "text": "CreateCrossClusterAPIKeyParams" + } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1150,7 +1172,7 @@ "signature": [ "SecurityCreateApiKeyResponse" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1163,7 +1185,7 @@ "label": "QueryApiKeySortOptions", "description": [], "signature": [ - "{ field: \"id\" | \"type\" | \"name\" | \"username\" | \"profile_uid\" | \"metadata\" | \"expired\" | \"creation\" | \"expiration\" | \"role_descriptors\" | \"realm\" | \"invalidated\" | \"realm_type\" | \"limited_by\" | \"_sort\"; direction: \"asc\" | \"desc\"; }" + "{ field: \"id\" | \"type\" | \"name\" | \"username\" | \"profile_uid\" | \"metadata\" | \"expired\" | \"creation\" | \"realm\" | \"role_descriptors\" | \"expiration\" | \"invalidated\" | \"realm_type\" | \"limited_by\" | \"_sort\"; direction: \"asc\" | \"desc\"; }" ], "path": "x-pack/packages/security/api_key_management/src/components/api_keys_api_client.ts", "deprecated": false, @@ -1180,9 +1202,31 @@ "\nRequest body of Kibana Update API key endpoint." ], "signature": [ - "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { id: string; role_descriptors: Record>; }> | Readonly<{ metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { id: string; type: \"cross_cluster\"; access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { id: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>" + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateRestAPIKeyParams", + "text": "UpdateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateCrossClusterAPIKeyParams", + "text": "UpdateCrossClusterAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams", + "text": "UpdateRestAPIKeyWithKibanaPrivilegesParams" + } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -1199,7 +1243,7 @@ "signature": [ "SecurityUpdateApiKeyResponse" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/kbn_security_api_key_management.mdx b/api_docs/kbn_security_api_key_management.mdx index 9c95241ecaa83..9de2544ada759 100644 --- a/api_docs/kbn_security_api_key_management.mdx +++ b/api_docs/kbn_security_api_key_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-api-key-management title: "@kbn/security-api-key-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-api-key-management plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-api-key-management'] --- import kbnSecurityApiKeyManagementObj from './kbn_security_api_key_management.devdocs.json'; diff --git a/api_docs/kbn_security_form_components.mdx b/api_docs/kbn_security_form_components.mdx index 27f10f009c4d4..3c1d11c315a2f 100644 --- a/api_docs/kbn_security_form_components.mdx +++ b/api_docs/kbn_security_form_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-form-components title: "@kbn/security-form-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-form-components plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-form-components'] --- import kbnSecurityFormComponentsObj from './kbn_security_form_components.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index bf16364236051..9f2daaf952d3b 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index ace424adb9be5..6698483b04855 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index 654c86d26238b..2d1b1c4829cdf 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.devdocs.json b/api_docs/kbn_security_plugin_types_server.devdocs.json index dbd001d28def1..7650af308a8fd 100644 --- a/api_docs/kbn_security_plugin_types_server.devdocs.json +++ b/api_docs/kbn_security_plugin_types_server.devdocs.json @@ -62,15 +62,15 @@ "label": "getRestApiKeyWithKibanaPrivilegesSchema", "description": [], "signature": [ - "(getBasePrivilegeNames: () => { global: string[]; space: string[]; }) => ExtendedObjectType<{ type: ", + "(getBasePrivilegeNames: () => { global: string[]; space: string[]; }) => ", { "pluginId": "@kbn/config-schema", "scope": "common", "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" + "section": "def-common.ObjectType", + "text": "ObjectType" }, - "<\"rest\" | undefined>; name: ", + "<{ type: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -78,7 +78,7 @@ "section": "def-common.Type", "text": "Type" }, - "; expiration: ", + "<\"rest\" | undefined>; name: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -86,7 +86,7 @@ "section": "def-common.Type", "text": "Type" }, - "; role_descriptors: ", + "; expiration: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -94,7 +94,7 @@ "section": "def-common.Type", "text": "Type" }, - ">>; metadata: ", + "; metadata: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -102,7 +102,7 @@ "section": "def-common.Type", "text": "Type" }, - " | undefined>; }, { role_descriptors: null; kibana_role_descriptors: ", + " | undefined>; kibana_role_descriptors: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -143,23 +143,15 @@ "label": "getUpdateRestApiKeyWithKibanaPrivilegesSchema", "description": [], "signature": [ - "(getBasePrivilegeNames: () => { global: string[]; space: string[]; }) => ExtendedObjectType<{ type: ", - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" - }, - "<\"rest\" | undefined>; name: ", + "(getBasePrivilegeNames: () => { global: string[]; space: string[]; }) => ", { "pluginId": "@kbn/config-schema", "scope": "common", "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" + "section": "def-common.ObjectType", + "text": "ObjectType" }, - "; expiration: ", + "<{ type: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -167,7 +159,7 @@ "section": "def-common.Type", "text": "Type" }, - "; role_descriptors: ", + "<\"rest\" | undefined>; expiration: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -175,7 +167,7 @@ "section": "def-common.Type", "text": "Type" }, - ">>; metadata: ", + "; metadata: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -183,7 +175,7 @@ "section": "def-common.Type", "text": "Type" }, - " | undefined>; }, { role_descriptors: null; name: null; id: ", + " | undefined>; id: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -223,6 +215,39 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.isCreateRestAPIKeyParams", + "type": "Function", + "tags": [], + "label": "isCreateRestAPIKeyParams", + "description": [], + "signature": [ + "(params: any) => boolean" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.isCreateRestAPIKeyParams.$1", + "type": "Any", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -536,8 +561,10 @@ "type": "Interface", "tags": [], "label": "APIKeys", - "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "description": [ + "\nInterface for managing API keys in Elasticsearch, including creation,\nvalidation, and invalidation of API keys,\nas well as checking the status of API key features." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -553,7 +580,7 @@ "signature": [ "() => Promise" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -571,7 +598,7 @@ "signature": [ "() => Promise" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -597,17 +624,17 @@ }, ", createParams: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CreateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", "text": "CreateAPIKeyParams" }, ") => Promise<", "SecurityCreateApiKeyResponse", " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -630,7 +657,7 @@ }, "" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -646,14 +673,98 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CreateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", "text": "CreateAPIKeyParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.update", + "type": "Function", + "tags": [], + "label": "update", + "description": [ + "\nAttempts update an API key with the provided 'role_descriptors' and 'metadata'\n\nReturns `updated`, `true` if the update was successful, `false` if there was nothing to update\n\nUser needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", updateParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + }, + ") => Promise<", + "SecurityUpdateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.update.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.update.$2", + "type": "CompoundType", + "tags": [], + "label": "updateParams", + "description": [ + "The params to edit an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -679,17 +790,33 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ", createParams: Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>) => Promise<", + ", createParams: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.GrantAPIKeyResult", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.GrantAPIKeyResult", "text": "GrantAPIKeyResult" }, " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -712,7 +839,7 @@ }, "" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -727,9 +854,23 @@ "Create operation parameters." ], "signature": [ - "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>" + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -749,15 +890,15 @@ "signature": [ "(apiKeyPrams: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.ValidateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", "text": "ValidateAPIKeyParams" }, ") => Promise" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -772,14 +913,14 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.ValidateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", "text": "ValidateAPIKeyParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -807,23 +948,23 @@ }, ", params: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" }, ") => Promise<", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeyResult", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", "text": "InvalidateAPIKeyResult" }, " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -846,7 +987,7 @@ }, "" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -862,14 +1003,14 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -889,23 +1030,23 @@ "signature": [ "(params: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" }, ") => Promise<", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeyResult", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", "text": "InvalidateAPIKeyResult" }, " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -920,14 +1061,14 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -940,45 +1081,569 @@ }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.AppActions", + "id": "def-server.APIKeys", "type": "Interface", "tags": [], - "label": "AppActions", - "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "label": "APIKeys", + "description": [ + "\nInterface for managing API keys in Elasticsearch, including creation,\nvalidation, and invalidation of API keys,\nas well as checking the status of API key features." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.AppActions.get", + "id": "def-server.APIKeys.areAPIKeysEnabled", "type": "Function", "tags": [], - "label": "get", - "description": [], + "label": "areAPIKeysEnabled", + "description": [ + "\nDetermines if API Keys are enabled in Elasticsearch." + ], "signature": [ - "(operation: string) => string" + "() => Promise" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.AppActions.get.$1", - "type": "string", - "tags": [], - "label": "operation", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.areCrossClusterAPIKeysEnabled", + "type": "Function", + "tags": [], + "label": "areCrossClusterAPIKeysEnabled", + "description": [ + "\nDetermines if Cross-Cluster API Keys are enabled in Elasticsearch." + ], + "signature": [ + "() => Promise" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [ + "\nTries to create an API key for the current user.\n\nReturns newly created API key or `null` if API keys are disabled.\n\nUser needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", createParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + }, + ") => Promise<", + "SecurityCreateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.create.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.create.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "The params to create an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", + "text": "CreateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.update", + "type": "Function", + "tags": [], + "label": "update", + "description": [ + "\nAttempts update an API key with the provided 'role_descriptors' and 'metadata'\n\nReturns `updated`, `true` if the update was successful, `false` if there was nothing to update\n\nUser needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", updateParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + }, + ") => Promise<", + "SecurityUpdateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.update.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.update.$2", + "type": "CompoundType", + "tags": [], + "label": "updateParams", + "description": [ + "The params to edit an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.grantAsInternalUser", + "type": "Function", + "tags": [], + "label": "grantAsInternalUser", + "description": [ + "\nTries to grant an API key for the current user." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", createParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.GrantAPIKeyResult", + "text": "GrantAPIKeyResult" + }, + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.grantAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.grantAsInternalUser.$2", + "type": "CompoundType", + "tags": [], + "label": "createParams", + "description": [ + "Create operation parameters." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.validate", + "type": "Function", + "tags": [], + "label": "validate", + "description": [ + "\nTries to validate an API key." + ], + "signature": [ + "(apiKeyPrams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + }, + ") => Promise" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.validate.$1", + "type": "Object", + "tags": [], + "label": "apiKeyPrams", + "description": [ + "ValidateAPIKeyParams." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", + "text": "ValidateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidate", + "type": "Function", + "tags": [], + "label": "invalidate", + "description": [ + "\nTries to invalidate an API keys." + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", params: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidate.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidate.$2", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate an API keys." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidateAsInternalUser", + "type": "Function", + "tags": [], + "label": "invalidateAsInternalUser", + "description": [ + "\nTries to invalidate the API keys by using the internal user." + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", + "text": "InvalidateAPIKeyResult" + }, + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.APIKeys.invalidateAsInternalUser.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "The params to invalidate the API keys." + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", + "text": "InvalidateAPIKeysParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AppActions", + "type": "Interface", + "tags": [], + "label": "AppActions", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AppActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(operation: string) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.AppActions.get.$1", + "type": "string", + "tags": [], + "label": "operation", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/app.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], "returnComment": [] } ], @@ -1550,10 +2215,10 @@ "description": [], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.APIKeys", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.APIKeys", "text": "APIKeys" } ], @@ -2169,35 +2834,257 @@ }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivileges.globally", + "id": "def-server.CheckPrivileges.globally", + "type": "Function", + "tags": [], + "label": "globally", + "description": [], + "signature": [ + "(privileges: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + }, + ", options?: ", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesResponse", + "text": "CheckPrivilegesResponse" + }, + ">" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.globally.$1", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesPayload", + "text": "CheckPrivilegesPayload" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivileges.globally.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/security-plugin-types-server", + "scope": "server", + "docId": "kibKbnSecurityPluginTypesServerPluginApi", + "section": "def-server.CheckPrivilegesOptions", + "text": "CheckPrivilegesOptions" + }, + " | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesOptions", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesOptions", + "description": [ + "\nOptions to influce the privilege checks." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesOptions.requireLoginAction", + "type": "CompoundType", + "tags": [], + "label": "requireLoginAction", + "description": [ + "\nWhether or not the `login` action should be required (default: true).\nSetting this to false is not advised except for special circumstances, when you do not require\nthe request to belong to a user capable of logging into Kibana." + ], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesPayload", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesPayload", + "description": [ + "\nPrivileges that can be checked for the Kibana users." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesPayload.kibana", + "type": "CompoundType", + "tags": [], + "label": "kibana", + "description": [ + "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." + ], + "signature": [ + "string | string[] | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesPayload.elasticsearch", + "type": "Object", + "tags": [], + "label": "elasticsearch", + "description": [ + "\nA set of the Elasticsearch cluster and index privileges." + ], + "signature": [ + "{ cluster: string[]; index: Record; } | undefined" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse", + "type": "Interface", + "tags": [], + "label": "CheckPrivilegesResponse", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse.hasAllRequested", + "type": "boolean", + "tags": [], + "label": "hasAllRequested", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse.username", + "type": "string", + "tags": [], + "label": "username", + "description": [], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckPrivilegesResponse.privileges", + "type": "Object", + "tags": [], + "label": "privileges", + "description": [], + "signature": [ + "{ kibana: { resource?: string | undefined; privilege: string; authorized: boolean; }[]; elasticsearch: { cluster: { privilege: string; authorized: boolean; }[]; index: { [indexName: string]: { privilege: string; authorized: boolean; }[]; }; }; }" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivileges", + "type": "Interface", + "tags": [], + "label": "CheckUserProfilesPrivileges", + "description": [ + "\nAn interface to check users profiles privileges in a specific context (only a single-space context is supported at\nthe moment)." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivileges.atSpace", "type": "Function", "tags": [], - "label": "globally", + "label": "atSpace", "description": [], "signature": [ - "(privileges: ", - { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckPrivilegesPayload", - "text": "CheckPrivilegesPayload" - }, - ", options?: ", + "(spaceId: string, privileges: ", { "pluginId": "@kbn/security-plugin-types-server", "scope": "server", "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckPrivilegesOptions", - "text": "CheckPrivilegesOptions" + "section": "def-server.CheckUserProfilesPrivilegesPayload", + "text": "CheckUserProfilesPrivilegesPayload" }, - " | undefined) => Promise<", + ") => Promise<", { "pluginId": "@kbn/security-plugin-types-server", "scope": "server", "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckPrivilegesResponse", - "text": "CheckPrivilegesResponse" + "section": "def-server.CheckUserProfilesPrivilegesResponse", + "text": "CheckUserProfilesPrivilegesResponse" }, ">" ], @@ -2207,19 +3094,13 @@ "children": [ { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivileges.globally.$1", - "type": "Object", + "id": "def-server.CheckUserProfilesPrivileges.atSpace.$1", + "type": "string", "tags": [], - "label": "privileges", + "label": "spaceId", "description": [], "signature": [ - { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckPrivilegesPayload", - "text": "CheckPrivilegesPayload" - } + "string" ], "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, @@ -2228,25 +3109,24 @@ }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivileges.globally.$2", + "id": "def-server.CheckUserProfilesPrivileges.atSpace.$2", "type": "Object", "tags": [], - "label": "options", + "label": "privileges", "description": [], "signature": [ { "pluginId": "@kbn/security-plugin-types-server", "scope": "server", "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckPrivilegesOptions", - "text": "CheckPrivilegesOptions" - }, - " | undefined" + "section": "def-server.CheckUserProfilesPrivilegesPayload", + "text": "CheckUserProfilesPrivilegesPayload" + } ], "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false, - "isRequired": false + "isRequired": true } ], "returnComment": [] @@ -2256,12 +3136,12 @@ }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesOptions", + "id": "def-server.CheckUserProfilesPrivilegesPayload", "type": "Interface", "tags": [], - "label": "CheckPrivilegesOptions", + "label": "CheckUserProfilesPrivilegesPayload", "description": [ - "\nOptions to influce the privilege checks." + "\nPrivileges that can be checked for the users profiles (only Kibana specific privileges are supported at the moment)." ], "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, @@ -2269,65 +3149,227 @@ "children": [ { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesOptions.requireLoginAction", - "type": "CompoundType", + "id": "def-server.CheckUserProfilesPrivilegesPayload.kibana", + "type": "Array", "tags": [], - "label": "requireLoginAction", + "label": "kibana", "description": [ - "\nWhether or not the `login` action should be required (default: true).\nSetting this to false is not advised except for special circumstances, when you do not require\nthe request to belong to a user capable of logging into Kibana." + "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." ], "signature": [ - "boolean | undefined" + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesResponse", + "type": "Interface", + "tags": [], + "label": "CheckUserProfilesPrivilegesResponse", + "description": [ + "\nResponse of the check privileges operation for the users profiles." + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesResponse.hasPrivilegeUids", + "type": "Array", + "tags": [], + "label": "hasPrivilegeUids", + "description": [ + "\nThe subset of the requested profile IDs of the users that have all the requested privileges." + ], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CheckUserProfilesPrivilegesResponse.errors", + "type": "Object", + "tags": [], + "label": "errors", + "description": [ + "\nAn errors object that may be returned from ES that contains a `count` of UIDs that have errors in the `details` property.\n\nEach entry in `details` will contain an error `type`, e.g 'resource_not_found_exception', and a `reason` message, e.g. 'profile document not found'" + ], + "signature": [ + "{ count: number; details: Record; } | undefined" ], "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesPayload", - "type": "Interface", - "tags": [], - "label": "CheckPrivilegesPayload", - "description": [ - "\nPrivileges that can be checked for the Kibana users." - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateCrossClusterAPIKeyParams", + "type": "Interface", + "tags": [], + "label": "CreateCrossClusterAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateCrossClusterAPIKeyParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"cross_cluster\"" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateCrossClusterAPIKeyParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateCrossClusterAPIKeyParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateCrossClusterAPIKeyParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateCrossClusterAPIKeyParams.access", + "type": "Object", + "tags": [], + "label": "access", + "description": [], + "signature": [ + "{ search?: { names: string[]; query?: unknown; field_security?: unknown; allow_restricted_indices?: boolean | undefined; }[] | undefined; replication?: { names: string[]; }[] | undefined; }" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateRestAPIKeyParams", + "type": "Interface", + "tags": [], + "label": "CreateRestAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateRestAPIKeyParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateRestAPIKeyParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateRestAPIKeyParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesPayload.kibana", - "type": "CompoundType", + "id": "def-server.CreateRestAPIKeyParams.role_descriptors", + "type": "Object", "tags": [], - "label": "kibana", - "description": [ - "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." - ], + "label": "role_descriptors", + "description": [], "signature": [ - "string | string[] | undefined" + "{ [x: string]: { [key: string]: any; }; }" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesPayload.elasticsearch", + "id": "def-server.CreateRestAPIKeyParams.metadata", "type": "Object", "tags": [], - "label": "elasticsearch", - "description": [ - "\nA set of the Elasticsearch cluster and index privileges." - ], + "label": "metadata", + "description": [], "signature": [ - "{ cluster: string[]; index: Record; } | undefined" + "{ [key: string]: any; } | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -2336,213 +3378,181 @@ }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesResponse", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams", "type": "Interface", "tags": [], - "label": "CheckPrivilegesResponse", + "label": "CreateRestAPIKeyWithKibanaPrivilegesParams", "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesResponse.hasAllRequested", - "type": "boolean", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.type", + "type": "string", "tags": [], - "label": "hasAllRequested", + "label": "type", "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesResponse.username", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.expiration", "type": "string", "tags": [], - "label": "username", + "label": "expiration", "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckPrivilegesResponse.privileges", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.metadata", "type": "Object", "tags": [], - "label": "privileges", + "label": "metadata", "description": [], "signature": [ - "{ kibana: { resource?: string | undefined; privilege: string; authorized: boolean; }[]; elasticsearch: { cluster: { privilege: string; authorized: boolean; }[]; index: { [indexName: string]: { privilege: string; authorized: boolean; }[]; }; }; }" + "{ [key: string]: any; } | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivileges", - "type": "Interface", - "tags": [], - "label": "CheckUserProfilesPrivileges", - "description": [ - "\nAn interface to check users profiles privileges in a specific context (only a single-space context is supported at\nthe moment)." - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivileges.atSpace", - "type": "Function", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.kibana_role_descriptors", + "type": "Object", "tags": [], - "label": "atSpace", + "label": "kibana_role_descriptors", "description": [], "signature": [ - "(spaceId: string, privileges: ", + "{ [x: string]: { elasticsearch: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckUserProfilesPrivilegesPayload", - "text": "CheckUserProfilesPrivilegesPayload" + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ElasticsearchPrivilegesType", + "text": "ElasticsearchPrivilegesType" }, - ") => Promise<", + " & { [key: string]: unknown; }; kibana: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckUserProfilesPrivilegesResponse", - "text": "CheckUserProfilesPrivilegesResponse" + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.KibanaPrivilegesType", + "text": "KibanaPrivilegesType" }, - ">" + "; }; }" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivileges.atSpace.$1", - "type": "string", - "tags": [], - "label": "spaceId", - "description": [], - "signature": [ - "string" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivileges.atSpace.$2", - "type": "Object", - "tags": [], - "label": "privileges", - "description": [], - "signature": [ - { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CheckUserProfilesPrivilegesPayload", - "text": "CheckUserProfilesPrivilegesPayload" - } - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false } ], "initialIsOpen": false }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivilegesPayload", + "id": "def-server.ElasticsearchPrivilegesType", "type": "Interface", "tags": [], - "label": "CheckUserProfilesPrivilegesPayload", + "label": "ElasticsearchPrivilegesType", "description": [ - "\nPrivileges that can be checked for the users profiles (only Kibana specific privileges are supported at the moment)." + "\nType representing Elasticsearch specific portion of the role definition." ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivilegesPayload.kibana", + "id": "def-server.ElasticsearchPrivilegesType.cluster", "type": "Array", "tags": [], - "label": "kibana", - "description": [ - "\nA list of the Kibana specific privileges (usually generated with `security.authz.actions.*.get(...)`)." - ], + "label": "cluster", + "description": [], "signature": [ - "string[]" + "string[] | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivilegesResponse", - "type": "Interface", - "tags": [], - "label": "CheckUserProfilesPrivilegesResponse", - "description": [ - "\nResponse of the check privileges operation for the users profiles." - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivilegesResponse.hasPrivilegeUids", + "id": "def-server.ElasticsearchPrivilegesType.remote_cluster", "type": "Array", "tags": [], - "label": "hasPrivilegeUids", - "description": [ - "\nThe subset of the requested profile IDs of the users that have all the requested privileges." + "label": "remote_cluster", + "description": [], + "signature": [ + "{ privileges: string[]; clusters: string[]; }[] | undefined" ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ElasticsearchPrivilegesType.indices", + "type": "Array", + "tags": [], + "label": "indices", + "description": [], "signature": [ - "string[]" + "{ names: string[]; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; privileges: string[]; query?: string | undefined; allow_restricted_indices?: boolean | undefined; }[] | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CheckUserProfilesPrivilegesResponse.errors", - "type": "Object", + "id": "def-server.ElasticsearchPrivilegesType.remote_indices", + "type": "Array", "tags": [], - "label": "errors", - "description": [ - "\nAn errors object that may be returned from ES that contains a `count` of UIDs that have errors in the `details` property.\n\nEach entry in `details` will contain an error `type`, e.g 'resource_not_found_exception', and a `reason` message, e.g. 'profile document not found'" + "label": "remote_indices", + "description": [], + "signature": [ + "{ clusters: string[]; names: string[]; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; privileges: string[]; query?: string | undefined; allow_restricted_indices?: boolean | undefined; }[] | undefined" ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.ElasticsearchPrivilegesType.run_as", + "type": "Array", + "tags": [], + "label": "run_as", + "description": [], "signature": [ - "{ count: number; details: Record; } | undefined" + "string[] | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/check_privileges.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false } @@ -2556,7 +3566,7 @@ "tags": [], "label": "GrantAPIKeyResult", "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2569,7 +3579,7 @@ "description": [ "\nUnique id for this API key" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2582,7 +3592,7 @@ "description": [ "\nName for this API key" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2595,7 +3605,7 @@ "description": [ "\nGenerated API key" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -2725,7 +3735,7 @@ "description": [ "\nThe return value when invalidating an API key in Elasticsearch." ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2741,7 +3751,7 @@ "signature": [ "string[]" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2757,7 +3767,7 @@ "signature": [ "string[]" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2770,7 +3780,7 @@ "description": [ "\nThe number of errors that were encountered when invalidating the API keys." ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -2786,7 +3796,7 @@ "signature": [ "{ type?: string | undefined; reason?: string | undefined; caused_by?: { type?: string | undefined; reason?: string | undefined; } | undefined; }[] | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -2802,7 +3812,7 @@ "description": [ "\nRepresents the params for invalidating multiple API keys" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2812,11 +3822,13 @@ "type": "Array", "tags": [], "label": "ids", - "description": [], + "description": [ + "\nList of unique API key IDs" + ], "signature": [ "string[]" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -3277,32 +4289,8 @@ "path": "x-pack/plugins/enterprise_search/server/lib/indices/create_api_key.ts" }, { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts" + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys.ts" }, { "plugin": "serverlessSearch", @@ -3403,30 +4391,6 @@ { "plugin": "fleet", "path": "x-pack/plugins/fleet/server/routes/setup/handlers.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/list.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/state.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts" } ] }, @@ -3543,65 +4507,324 @@ "children": [ { "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.UIActions.get", - "type": "Function", + "id": "def-server.UIActions.get", + "type": "Function", + "tags": [], + "label": "get", + "description": [], + "signature": [ + "(featureId: keyof ", + { + "pluginId": "@kbn/core-capabilities-common", + "scope": "common", + "docId": "kibKbnCoreCapabilitiesCommonPluginApi", + "section": "def-common.Capabilities", + "text": "Capabilities" + }, + ", ...uiCapabilityParts: string[]) => string" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UIActions.get.$1", + "type": "CompoundType", + "tags": [], + "label": "featureId", + "description": [], + "signature": [ + "keyof ", + { + "pluginId": "@kbn/core-capabilities-common", + "scope": "common", + "docId": "kibKbnCoreCapabilitiesCommonPluginApi", + "section": "def-common.Capabilities", + "text": "Capabilities" + } + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UIActions.get.$2", + "type": "Array", + "tags": [], + "label": "uiCapabilityParts", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateCrossClusterAPIKeyParams", + "type": "Interface", + "tags": [], + "label": "UpdateCrossClusterAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateCrossClusterAPIKeyParams.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateCrossClusterAPIKeyParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"cross_cluster\"" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateCrossClusterAPIKeyParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateCrossClusterAPIKeyParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateCrossClusterAPIKeyParams.access", + "type": "Object", + "tags": [], + "label": "access", + "description": [], + "signature": [ + "{ search?: { names: string[]; query?: unknown; field_security?: unknown; allow_restricted_indices?: boolean | undefined; }[] | undefined; replication?: { names: string[]; }[] | undefined; }" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyParams", + "type": "Interface", + "tags": [], + "label": "UpdateRestAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyParams.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyParams.role_descriptors", + "type": "Object", + "tags": [], + "label": "role_descriptors", + "description": [], + "signature": [ + "{ [x: string]: { [key: string]: unknown; }; }" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyWithKibanaPrivilegesParams", + "type": "Interface", + "tags": [], + "label": "UpdateRestAPIKeyWithKibanaPrivilegesParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyWithKibanaPrivilegesParams.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyWithKibanaPrivilegesParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyWithKibanaPrivilegesParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyWithKibanaPrivilegesParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/security-plugin-types-server", + "id": "def-server.UpdateRestAPIKeyWithKibanaPrivilegesParams.kibana_role_descriptors", + "type": "Object", "tags": [], - "label": "get", + "label": "kibana_role_descriptors", "description": [], "signature": [ - "(featureId: keyof ", + "{ [x: string]: { elasticsearch: ", { - "pluginId": "@kbn/core-capabilities-common", + "pluginId": "@kbn/core-security-server", "scope": "common", - "docId": "kibKbnCoreCapabilitiesCommonPluginApi", - "section": "def-common.Capabilities", - "text": "Capabilities" + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ElasticsearchPrivilegesType", + "text": "ElasticsearchPrivilegesType" }, - ", ...uiCapabilityParts: string[]) => string" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + " & { [key: string]: unknown; }; kibana: ", { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.UIActions.get.$1", - "type": "CompoundType", - "tags": [], - "label": "featureId", - "description": [], - "signature": [ - "keyof ", - { - "pluginId": "@kbn/core-capabilities-common", - "scope": "common", - "docId": "kibKbnCoreCapabilitiesCommonPluginApi", - "section": "def-common.Capabilities", - "text": "Capabilities" - } - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.KibanaPrivilegesType", + "text": "KibanaPrivilegesType" }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.UIActions.get.$2", - "type": "Array", - "tags": [], - "label": "uiCapabilityParts", - "description": [], - "signature": [ - "string[]" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/actions/ui.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } + "; }; }" ], - "returnComment": [] + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -3866,7 +5089,7 @@ "description": [ "\nRepresents the parameters for validating API Key credentials." ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -3879,7 +5102,7 @@ "description": [ "\nUnique id for this API key" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -3892,7 +5115,7 @@ "description": [ "\nGenerated API Key (secret)" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -4209,9 +5432,31 @@ "label": "CreateAPIKeyParams", "description": [], "signature": [ - "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }> | Readonly<{ metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { type: \"cross_cluster\"; name: string; access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }>" + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateCrossClusterAPIKeyParams", + "text": "CreateCrossClusterAPIKeyParams" + } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -4228,67 +5473,7 @@ "signature": [ "SecurityCreateApiKeyResponse" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CreateCrossClusterAPIKeyParams", - "type": "Type", - "tags": [], - "label": "CreateCrossClusterAPIKeyParams", - "description": [], - "signature": [ - "{ readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly type: \"cross_cluster\"; readonly name: string; readonly access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CreateRestAPIKeyParams", - "type": "Type", - "tags": [], - "label": "CreateRestAPIKeyParams", - "description": [], - "signature": [ - "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly role_descriptors: Record>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams", - "type": "Type", - "tags": [], - "label": "CreateRestAPIKeyWithKibanaPrivilegesParams", - "description": [], - "signature": [ - "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.ElasticsearchPrivilegesType", - "type": "Type", - "tags": [], - "label": "ElasticsearchPrivilegesType", - "description": [], - "signature": [ - "{ readonly cluster?: string[] | undefined; readonly indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; readonly remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; readonly remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; readonly run_as?: string[] | undefined; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -4314,11 +5499,13 @@ "type": "Type", "tags": [], "label": "KibanaPrivilegesType", - "description": [], + "description": [ + "\nType representing Kibana specific portion of the role definition." + ], "signature": [ - "Readonly<{ base?: string[] | undefined; feature?: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]" + "{ spaces: string[]; base?: string[] | undefined; feature?: Record | undefined; }[]" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -4333,9 +5520,31 @@ "\nRequest body of Kibana Update API key endpoint." ], "signature": [ - "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { id: string; role_descriptors: Record>; }> | Readonly<{ metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { id: string; type: \"cross_cluster\"; access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { id: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>" + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateRestAPIKeyParams", + "text": "UpdateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateCrossClusterAPIKeyParams", + "text": "UpdateCrossClusterAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateRestAPIKeyWithKibanaPrivilegesParams", + "text": "UpdateRestAPIKeyWithKibanaPrivilegesParams" + } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -4352,52 +5561,7 @@ "signature": [ "SecurityUpdateApiKeyResponse" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.UpdateCrossClusterAPIKeyParams", - "type": "Type", - "tags": [], - "label": "UpdateCrossClusterAPIKeyParams", - "description": [], - "signature": [ - "{ readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly id: string; readonly type: \"cross_cluster\"; readonly access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.UpdateRestAPIKeyParams", - "type": "Type", - "tags": [], - "label": "UpdateRestAPIKeyParams", - "description": [], - "signature": [ - "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly id: string; readonly role_descriptors: Record>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/security-plugin-types-server", - "id": "def-server.UpdateRestAPIKeyWithKibanaPrivilegesParams", - "type": "Type", - "tags": [], - "label": "UpdateRestAPIKeyWithKibanaPrivilegesParams", - "description": [], - "signature": [ - "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly id: string; readonly kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -4442,7 +5606,7 @@ "section": "def-common.ObjectType", "text": "ObjectType" }, - "; name: ", + "<\"cross_cluster\">; name: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4466,23 +5630,7 @@ "section": "def-common.Type", "text": "Type" }, - "; role_descriptors: ", - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" - }, - ">>; metadata: ", - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" - }, - " | undefined>; }, { type: ", + "; metadata: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4490,7 +5638,7 @@ "section": "def-common.Type", "text": "Type" }, - "<\"cross_cluster\">; role_descriptors: null; access: ", + " | undefined>; access: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4514,7 +5662,7 @@ "section": "def-common.Type", "text": "Type" }, - "[] | undefined>; }>; }>>" + "[] | undefined>; }>; }>" ], "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, @@ -4662,23 +5810,7 @@ "section": "def-common.ObjectType", "text": "ObjectType" }, - "; name: ", - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" - }, - "; expiration: ", + "<{ id: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4686,7 +5818,7 @@ "section": "def-common.Type", "text": "Type" }, - "; role_descriptors: ", + "; type: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4694,7 +5826,7 @@ "section": "def-common.Type", "text": "Type" }, - ">>; metadata: ", + "<\"cross_cluster\">; expiration: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4702,7 +5834,7 @@ "section": "def-common.Type", "text": "Type" }, - " | undefined>; }, { type: ", + "; metadata: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4710,7 +5842,7 @@ "section": "def-common.Type", "text": "Type" }, - "<\"cross_cluster\">; role_descriptors: null; access: ", + " | undefined>; access: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4734,15 +5866,7 @@ "section": "def-common.Type", "text": "Type" }, - "[] | undefined>; }>; }>, { name: null; id: ", - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" - }, - "; }>>" + "[] | undefined>; }>; }>" ], "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, @@ -4764,7 +5888,7 @@ "section": "def-common.ObjectType", "text": "ObjectType" }, - "; name: ", + "; type: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4780,7 +5904,7 @@ "section": "def-common.Type", "text": "Type" }, - "; expiration: ", + "<\"rest\" | undefined>; expiration: ", { "pluginId": "@kbn/config-schema", "scope": "common", @@ -4804,15 +5928,7 @@ "section": "def-common.Type", "text": "Type" }, - " | undefined>; }, { name: null; id: ", - { - "pluginId": "@kbn/config-schema", - "scope": "common", - "docId": "kibKbnConfigSchemaPluginApi", - "section": "def-common.Type", - "text": "Type" - }, - "; }>>" + " | undefined>; }>" ], "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", "deprecated": false, diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index 7c6aa2da3511c..3e7061f677932 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 216 | 0 | 121 | 0 | +| 275 | 1 | 154 | 0 | ## Server diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index b21ee6f8c4925..dfbe39ed26008 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 0b83757f314ee..1c82c1fb5fab6 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 8057980cadb7a..96abbad087170 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index b5b0d157c24ac..7e64d4df30fde 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 5dd49e562276d..2cc69fe03cf35 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 847ada8f39c8f..f5ddcbbdec00f 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index e9d5e398486b6..0d015da9e8c91 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 57d5f1abad190..c4d3ed200cb01 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index fff4914fe6a2f..523d9999543fa 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 954af3189ec59..75effaccc574a 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 5d0f2d29fcb49..1e6a0aabed154 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index adde037bf8f76..4c8aae94e6133 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index e43f20cdf9b71..490c848990154 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 70fb3ceefef02..ae97799bc3aba 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 26f187818b0d1..faf11f193c183 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 100e35128e49b..342491854dedd 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index b2150310488de..55361e2a7237b 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 8dc005863aaae..1080ee46832bc 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 0334b7101d410..bcb12e8d0cfa7 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 0974a12d88dd5..3cc6dfa8ac1e9 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 17e8687117b28..6070497ba89f5 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 906161232d75c..bfed07e3aee98 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index a651a0ed8e596..5fb1689a44c0e 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 52f01f02c37a5..37268e7c5d084 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index b34d7c3f130ad..dcf3b3e7304fa 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 0654aa98964e3..573c06ab10754 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index b95f3d8c270a9..8da691192d30c 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 87842d991d974..32592b9530781 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index f21be827265cb..4b103166e9861 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index ae711cf27009d..0ae891156019a 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index ce8b7138b3f6a..400fabf3a166e 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 2b9f134fc1a71..997ed2a4ff707 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index f6b0046b47e5f..a658f1ba95d35 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 714b36ef1c651..bd4c4f5703aa1 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 3c6075bbedff6..43361f2463328 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index 3f830a73d4ac2..6ce92b6cf411a 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index bf856b962f65d..058ca848fa53a 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index dd93e338a49b6..afabf5111ee83 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index e06be2cf1ae86..5e76afeaeafac 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 5bcc1d6dd2210..bb3b7effbf9c8 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 7a390a371b985..1b00907123e87 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index b3eddac900238..aaa49e0082044 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index b98be1af3526a..4cae17904f628 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 49889a486b35e..d0f5305b3a0e3 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index fe5d366e3204d..2d7045010a304 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index e7e61258e8112..0ca47d74e2d82 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 7c1a0a4799703..3c3351144621d 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 1d8f6e8a14c32..f309286b16a73 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 8f4ffc93536e3..4bb82443adc55 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index f90b3d07d556e..e5ff02b811d69 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 409ea87af405c..38badc96b8e15 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 3599ac1d45c67..e7642c35ca433 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 8a8fbf4076232..914449bc2a88a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 0a99623c45ee7..52ef3c3592ba5 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index a003943ace5fe..0474b58e8bf76 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index d811eb76f4f32..40eef9e7e6769 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 9807cff5789c2..fddc72556aa1d 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 937c53dd2f21b..d6a56bb022bb8 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index 3f77f2e595402..d223d7704040f 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 4c55d88218b17..34a962b8ecb3a 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 29e6f400f9019..f1dc1b7bdca18 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 507995b46b07e..713cb9f1bc23d 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 8d2e867e0829d..2e7305429d011 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 69aee6a46de94..f17a5d8e7f7d4 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 5296751e7f6c1..909854dc37fb6 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index ca374d345179f..9c97e78796c88 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index b7a7ca637f243..516e98c6ed266 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index 807a274717f06..d723cb532045d 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 0db9b623abdb4..4af82c552186c 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index e10cfefa7e37f..2a496ddbfa6d9 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 5d4f631d15a18..f64585581fb59 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index 3871ca4aab266..572f4d38cd2b1 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 0745405dc9a15..531a7d9f3f299 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index ff38938a9cf6a..9d0d9644a0539 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index 54d1eca651229..7e01852d0d379 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index cb36987434044..153170ae15de1 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index b25472babcf81..b8cbca02207f7 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.devdocs.json b/api_docs/kbn_test_eui_helpers.devdocs.json index aab4d099fcc9a..d3276c2634aba 100644 --- a/api_docs/kbn_test_eui_helpers.devdocs.json +++ b/api_docs/kbn_test_eui_helpers.devdocs.json @@ -140,7 +140,7 @@ "tags": [], "label": "selected", "description": [ - "\nReturns selected value of button group" + "\nReturns selected option of button group" ], "signature": [ "HTMLElement" @@ -186,6 +186,171 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness", + "type": "Class", + "tags": [], + "label": "EuiSelectTestHarness", + "description": [], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.testId", + "type": "string", + "tags": [], + "label": "#testId", + "description": [], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.selectEl", + "type": "Object", + "tags": [], + "label": "#selectEl", + "description": [ + "\nReturns select or throws" + ], + "signature": [ + "HTMLElement" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.Unnamed.$1", + "type": "string", + "tags": [], + "label": "testId", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.testId", + "type": "string", + "tags": [], + "label": "testId", + "description": [ + "\nReturns `data-test-subj` of select" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.self", + "type": "CompoundType", + "tags": [], + "label": "self", + "description": [ + "\nReturns button select if found, otherwise `null`" + ], + "signature": [ + "HTMLElement | null" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [ + "\nReturns all options of select" + ], + "signature": [ + "HTMLOptionElement[]" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.selected", + "type": "string", + "tags": [], + "label": "selected", + "description": [ + "\nReturns selected option" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.select", + "type": "Function", + "tags": [], + "label": "select", + "description": [ + "\nSelect option by value" + ], + "signature": [ + "(optionName: string | RegExp) => void" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test-eui-helpers", + "id": "def-common.EuiSelectTestHarness.select.$1", + "type": "CompoundType", + "tags": [], + "label": "optionName", + "description": [], + "signature": [ + "string | RegExp" + ], + "path": "packages/kbn-test-eui-helpers/src/rtl_helpers.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/test-eui-helpers", "id": "def-common.EuiSuperDatePickerTestHarness", diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index f96a386cb8ed1..6c562a0c1b9a3 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 25 | 0 | 13 | 0 | +| 36 | 0 | 18 | 0 | ## Common diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 93131030a565f..b973f23ecc14e 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index ebab933870827..282504963c042 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index 5a4309ae18d55..a658d88790f29 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index 709a03157915b..95919e349f2e8 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index dc160105e78a5..5100c90c43a4d 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index 636b8be20610f..74dd99c686fe5 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index 949a959470f6e..86cbfe71989d6 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 3ce429bac4a27..48c59ca0bc025 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 4863e314d89a5..bb1e9dd0ff611 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 303b7a7721882..a6f1a9ca28495 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.devdocs.json b/api_docs/kbn_ui_shared_deps_src.devdocs.json index 5a4f7663f18a0..d80ea1cd158c8 100644 --- a/api_docs/kbn_ui_shared_deps_src.devdocs.json +++ b/api_docs/kbn_ui_shared_deps_src.devdocs.json @@ -342,6 +342,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/ui-shared-deps-src", + "id": "def-common.externals.fastestlevenshtein", + "type": "string", + "tags": [], + "label": "'fastest-levenshtein'", + "description": [], + "path": "packages/kbn-ui-shared-deps-src/src/definitions.js", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/ui-shared-deps-src", "id": "def-common.externals.rxjs", @@ -524,6 +535,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/ui-shared-deps-src", + "id": "def-common.externals.kbncryptobrowser", + "type": "string", + "tags": [], + "label": "'@kbn/crypto-browser'", + "description": [], + "path": "packages/kbn-ui-shared-deps-src/src/definitions.js", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/ui-shared-deps-src", "id": "def-common.externals.kbnesquery", diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 7c0e9167c87a6..d35d8cab4a032 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 55 | 0 | 46 | 0 | +| 57 | 0 | 48 | 0 | ## Common diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 86fda35b01e2c..f110f52a44591 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index d69f9c8c5c1a8..0ba9e177c6af7 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index b29a49446008f..d285fd99e8b66 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 7d5a9c5415762..1c7b53d1945d5 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index cff3206d2d904..a4afc63d92d45 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_prompt.mdx b/api_docs/kbn_unsaved_changes_prompt.mdx index 9a07953f5ef7b..ec0570e3e2d31 100644 --- a/api_docs/kbn_unsaved_changes_prompt.mdx +++ b/api_docs/kbn_unsaved_changes_prompt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-prompt title: "@kbn/unsaved-changes-prompt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-prompt plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-prompt'] --- import kbnUnsavedChangesPromptObj from './kbn_unsaved_changes_prompt.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 3a6350ce0f461..e19ce298d1d99 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 4d51c61d4f468..6a2ef5267d5d6 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 3a8b5546675b9..2be5b84d99b5d 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 6016643b3d27e..e21d08b1df36e 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index 16358c94b7da8..610b0552aaec8 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index 16b7d652dfe9b..fdb610951ae99 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index 35cd7528e1757..f15b60025e25c 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 7cd72ea98a4e0..7d775c092d435 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 6f87b3d59c226..f7c3bbfd7710d 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 01deacde43e62..d1ab58512efea 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index f994ec347d18b..9d52f95b68f65 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 3019bde1572f7..2edf16ed37065 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 431ebf3a2d618..05bcd7119c127 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 7d4b442e3b28a..87a253ec3a36c 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index 28c0c308162ca..0534b92e8b64c 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -4455,6 +4455,63 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "lens", + "id": "def-public.MetricVisualizationState.titlesTextAlign", + "type": "CompoundType", + "tags": [], + "label": "titlesTextAlign", + "description": [], + "signature": [ + "\"right\" | \"left\" | \"center\" | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/metric/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.MetricVisualizationState.valuesTextAlign", + "type": "CompoundType", + "tags": [], + "label": "valuesTextAlign", + "description": [], + "signature": [ + "\"right\" | \"left\" | \"center\" | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/metric/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.MetricVisualizationState.iconAlign", + "type": "CompoundType", + "tags": [], + "label": "iconAlign", + "description": [], + "signature": [ + "\"right\" | \"left\" | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/metric/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.MetricVisualizationState.valueFontMode", + "type": "CompoundType", + "tags": [], + "label": "valueFontMode", + "description": [], + "signature": [ + "ValueFontMode", + " | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/metric/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "lens", "id": "def-public.MetricVisualizationState.color", @@ -4469,6 +4526,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "lens", + "id": "def-public.MetricVisualizationState.icon", + "type": "string", + "tags": [], + "label": "icon", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/metric/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "lens", "id": "def-public.MetricVisualizationState.palette", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index c3d5cb143c0e7..dfed4b921904a 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 672 | 0 | 570 | 62 | +| 677 | 0 | 575 | 63 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index de74ce08f2e16..5287fcc22423c 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 09495a17a5d3b..c63e0fec1868a 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 4fc4c85abc9f5..805961d08f569 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index 560e44067a7ea..ef7ea92166ec3 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index a2d3af9cc76f5..2ffa82c931dd4 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 035befc30e241..b9cdbfba6e59b 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index b1b1874c1a108..a1f93ab35aae0 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 455b1b62caf67..d753ec97f3141 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 9efd852c72ca9..88e95befdc61e 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 47e0f72e43f1b..46d7c7405c2a7 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 1541454bbeea5..cea152f5e2b51 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index 94a27bc9aec2d..0b09dfea10e80 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 380e40ca6d566..b2807f2a051a2 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index d6d2f2c8ac194..4909a1e634bc6 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index dcbdf08271312..fcba5a7a72a94 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 74d85a0245195..cb0a3d4ca3335 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 380cd978db995..ef2ba553deac4 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index bd93f2dc714f9..c0696a43adf04 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index f79178e58cd42..2dd0b5b8da8e3 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 5e5755bad1a4d..c8b506954ee61 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 3449ee952dca5..18ae21442cfea 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index d2d8baa627bcb..94f0747e06a39 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 1b834afc8f332..a5734f975170e 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 9c33efa5ef117..80f7c77827d38 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index 231481cbb3217..6c61de8b5b015 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 02f2807beb89d..7492afaa416fd 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index dd1bbf734cd35..6d3111a0ac489 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 9e1a61f471a7c..a566d206689c1 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index aa0bef6692c8e..1617fdc10a4c9 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index d152b01f9130a..37f80c7e739e9 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 811 | 695 | 42 | +| 812 | 696 | 42 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 49694 | 237 | 37903 | 1897 | +| 49923 | 239 | 38040 | 1897 | ## Plugin Directory @@ -57,7 +57,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 129 | 0 | 123 | 12 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3199 | 31 | 2590 | 24 | -| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 8 | 0 | 8 | 0 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 5 | 0 | 5 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 35 | 0 | 25 | 5 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Reusable data view field editor across Kibana | 72 | 0 | 33 | 1 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data view management app | 2 | 0 | 2 | 0 | @@ -74,20 +74,21 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Extends embeddable plugin with more functionality | 19 | 0 | 19 | 2 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 53 | 0 | 46 | 1 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | Adds dashboards for discovering and managing Enterprise Search products. | 5 | 0 | 5 | 0 | -| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | Entity manager plugin for entity assets (inventory, topology, etc) | 8 | 0 | 8 | 1 | +| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | Entity manager plugin for entity assets (inventory, topology, etc) | 14 | 0 | 14 | 1 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 99 | 3 | 97 | 3 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 29 | 0 | 10 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 2 | 0 | 2 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | The Event Annotation service contains expressions for event annotations | 201 | 0 | 201 | 6 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | The listing page for event annotations. | 15 | 0 | 15 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 112 | 0 | 112 | 11 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 126 | 0 | 126 | 12 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 126 | 0 | 126 | 12 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'error' renderer to expressions | 17 | 0 | 15 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Gauge plugin adds a `gauge` renderer and function to the expression plugin. The renderer will display the `gauge` chart. | 59 | 0 | 58 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Heatmap plugin adds a `heatmap` renderer and function to the expression plugin. The renderer will display the `heatmap` chart. | 112 | 0 | 108 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'image' function and renderer to expressions | 26 | 0 | 26 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Adds a `metric` renderer and function to the expression plugin. The renderer will display the `legacy metric` chart. | 51 | 0 | 51 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'metric' function and renderer to expressions | 32 | 0 | 27 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart. | 67 | 0 | 67 | 2 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart. | 75 | 0 | 75 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Partition Visualization plugin adds a `partitionVis` renderer and `pieVis`, `mosaicVis`, `treemapVis`, `waffleVis` functions to the expression plugin. The renderer will display the `pie`, `waffle`, `treemap` and `mosaic` charts. | 73 | 0 | 73 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'repeatImage' function and renderer to expressions | 32 | 0 | 32 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'revealImage' function and renderer to expressions | 14 | 0 | 14 | 3 | @@ -101,7 +102,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 84 | 0 | 84 | 8 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 24 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 3 | 0 | 3 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1348 | 5 | 1226 | 72 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1349 | 5 | 1227 | 72 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 72 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -119,13 +120,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 127 | 2 | 100 | 4 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Plugin implementing the Integration Assistant API and UI | 47 | 0 | 40 | 3 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides UI and APIs for the interactive setup mode. | 28 | 0 | 18 | 0 | -| | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 95 | 0 | 95 | 4 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 95 | 0 | 95 | 4 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 6 | 0 | 6 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 153 | 0 | 121 | 3 | | kibanaUsageCollection | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 610 | 3 | 417 | 9 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | - | 5 | 0 | 5 | 1 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 672 | 0 | 570 | 62 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 677 | 0 | 575 | 63 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 8 | 0 | 8 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -177,9 +178,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 18 | 0 | 10 | 0 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 10 | 0 | 6 | 1 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | Plugin to provide access to and rendering of python notebooks for use in the persistent developer console. | 8 | 0 | 8 | 1 | -| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 18 | 0 | 10 | 1 | +| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 15 | 0 | 9 | 1 | | searchprofiler | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 415 | 0 | 206 | 1 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 438 | 0 | 222 | 1 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 191 | 0 | 121 | 37 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | ESS customizations for Security Solution. | 6 | 0 | 6 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Serverless customizations for security. | 7 | 0 | 7 | 0 | @@ -193,13 +194,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 260 | 0 | 66 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 25 | 0 | 25 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 10 | 0 | 10 | 0 | -| synthetics | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | +| synthetics | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 102 | 0 | 59 | 5 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 45 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 31 | 0 | 26 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 0 | 0 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 29 | 0 | 10 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 30 | 0 | 14 | 4 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 228 | 1 | 184 | 18 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | @@ -211,7 +211,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | The `unifiedHistogram` plugin provides UI components to create a layout including a resizable histogram and a main display. | 71 | 0 | 36 | 6 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 152 | 2 | 113 | 23 | | upgradeAssistant | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | This plugin visualizes data from Heartbeat, and integrates with other Observability solutions. | 1 | 0 | 1 | 0 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | This plugin visualizes data from Heartbeat, and integrates with other Observability solutions. | 1 | 0 | 1 | 0 | | urlDrilldown | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds drilldown implementations to Kibana | 0 | 0 | 0 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 56 | 0 | 16 | 2 | @@ -278,7 +278,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 10 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 48 | 0 | 32 | 3 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 49 | 0 | 33 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 30 | 0 | 30 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 195 | 1 | 128 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 101 | 0 | 0 | 0 | @@ -418,9 +418,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 8 | 0 | 8 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 8 | 0 | 8 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 20 | 0 | 6 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 52 | 0 | 16 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 146 | 1 | 63 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 16 | 0 | 16 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 13 | 0 | 13 | 2 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 15 | 0 | 15 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 21 | 0 | 20 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 20 | 0 | 3 | 0 | @@ -461,14 +461,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 51 | 0 | 51 | 1 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 14 | 0 | 9 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 80 | 0 | 80 | 1 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 5 | 0 | 4 | 0 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 7 | 0 | 5 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 44 | 0 | 43 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 7 | 0 | 7 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 5 | 0 | 5 | 0 | | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 4 | 0 | 4 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 3 | 0 | 3 | 0 | -| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 55 | 0 | 43 | 0 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 56 | 0 | 44 | 0 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 17 | 0 | 17 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 5 | 0 | 5 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | @@ -490,14 +490,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 35 | 0 | 34 | 0 | | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 160 | 0 | 134 | 9 | | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 362 | 0 | 336 | 0 | -| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 18 | 0 | 18 | 0 | +| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 19 | 0 | 19 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 52 | 0 | 37 | 7 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 32 | 0 | 19 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 6 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 269 | 1 | 209 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 26 | 0 | 26 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 99 | 1 | 96 | 11 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 104 | 1 | 101 | 10 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 53 | 0 | 51 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 193 | 0 | 182 | 10 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 39 | 0 | @@ -608,6 +608,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 18 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 11 | 0 | 2 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 11 | 0 | 8 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 14 | 0 | 7 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 18 | 0 | 18 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 40 | 0 | 38 | 5 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 13 | 0 | 9 | 0 | @@ -643,7 +644,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 7 | 0 | 7 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 118 | 0 | 59 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 51 | 0 | 25 | 0 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 216 | 0 | 121 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 275 | 1 | 154 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 14 | 0 | 14 | 6 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 54 | 0 | 49 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 30 | 0 | 24 | 0 | @@ -721,7 +722,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 41 | 2 | 21 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 5 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 315 | 4 | 267 | 13 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 25 | 0 | 13 | 0 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 36 | 0 | 18 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 131 | 3 | 98 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 33 | 0 | 13 | 0 | @@ -732,7 +733,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 39 | 0 | 25 | 1 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 86 | 0 | 86 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 42 | 0 | 28 | 0 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 55 | 0 | 46 | 0 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 57 | 0 | 48 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 9 | 0 | 8 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the unified data table which can be integrated into apps | 153 | 0 | 81 | 1 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 18 | 0 | 17 | 5 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index 5377b64c59d8b..6020b7134ac6b 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 2cf99c9268efc..b44845d2bccf5 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 5fc1054893379..af1b22b1f5a80 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index c81cc064925b0..bc8b55f94b14b 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 61ae6d3ac7bc4..4c345e18a0347 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index e85a65b36c59f..fc5f161857818 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 905ccaead09c7..a5cab8c2c7904 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index fe3d512ee6f20..d0778caa91448 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 92cd07cb6a86d..fc469b1bdf9f4 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index c16fb0c31ecab..b4fcaecdbef71 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 2d65a9e00b0a4..6ab7e6becdfb1 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 47bc1753e03e3..04f035cbea41b 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 343c998820d52..c4aba5bd77306 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index d42770557cf89..492ce4fcb5e10 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index f2d736be63446..ee3b581ca164c 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 850b0930150ee..2b108c8375985 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 5aa11ebce0055..e5be4f65b96d0 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index fb4ee5e644b26..0f622d71afe27 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_homepage.mdx b/api_docs/search_homepage.mdx index a0d0813d0d9c9..7fe0ca94d99a0 100644 --- a/api_docs/search_homepage.mdx +++ b/api_docs/search_homepage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchHomepage title: "searchHomepage" image: https://source.unsplash.com/400x175/?github description: API docs for the searchHomepage plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchHomepage'] --- import searchHomepageObj from './search_homepage.devdocs.json'; diff --git a/api_docs/search_inference_endpoints.mdx b/api_docs/search_inference_endpoints.mdx index 1d24c2f2d0127..80170001eb1ad 100644 --- a/api_docs/search_inference_endpoints.mdx +++ b/api_docs/search_inference_endpoints.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchInferenceEndpoints title: "searchInferenceEndpoints" image: https://source.unsplash.com/400x175/?github description: API docs for the searchInferenceEndpoints plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchInferenceEndpoints'] --- import searchInferenceEndpointsObj from './search_inference_endpoints.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index a2ca0c503916f..67d86174c1e37 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.devdocs.json b/api_docs/search_playground.devdocs.json index 0306aa3c5780a..9acc1abcd944f 100644 --- a/api_docs/search_playground.devdocs.json +++ b/api_docs/search_playground.devdocs.json @@ -40,9 +40,7 @@ "label": "PlaygroundProvider", "description": [], "signature": [ - "React.FunctionComponent<", - "PlaygroundProviderProps", - " & { children?: React.ReactNode; }>" + "React.FunctionComponent<{ children?: React.ReactNode; }>" ], "path": "x-pack/plugins/search_playground/public/types.ts", "deprecated": false, @@ -79,51 +77,6 @@ } ] }, - { - "parentPluginId": "searchPlayground", - "id": "def-public.SearchPlaygroundPluginStart.PlaygroundToolbar", - "type": "Function", - "tags": [], - "label": "PlaygroundToolbar", - "description": [], - "signature": [ - "React.FunctionComponent<{ children?: React.ReactNode; }>" - ], - "path": "x-pack/plugins/search_playground/public/types.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "searchPlayground", - "id": "def-public.SearchPlaygroundPluginStart.PlaygroundToolbar.$1", - "type": "CompoundType", - "tags": [], - "label": "props", - "description": [], - "signature": [ - "P & { children?: React.ReactNode; }" - ], - "path": "node_modules/@types/react/index.d.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "searchPlayground", - "id": "def-public.SearchPlaygroundPluginStart.PlaygroundToolbar.$2", - "type": "Any", - "tags": [], - "label": "context", - "description": [], - "signature": [ - "any" - ], - "path": "node_modules/@types/react/index.d.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, { "parentPluginId": "searchPlayground", "id": "def-public.SearchPlaygroundPluginStart.Playground", @@ -132,7 +85,9 @@ "label": "Playground", "description": [], "signature": [ - "React.FunctionComponent<{ children?: React.ReactNode; }>" + "React.FunctionComponent<", + "AppProps", + " & { children?: React.ReactNode; }>" ], "path": "x-pack/plugins/search_playground/public/types.ts", "deprecated": false, diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 64611dafd8273..193273e6d614e 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-ki | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 18 | 0 | 10 | 1 | +| 15 | 0 | 9 | 1 | ## Client diff --git a/api_docs/security.devdocs.json b/api_docs/security.devdocs.json index 6923618136dcd..5d7daa39f0360 100644 --- a/api_docs/security.devdocs.json +++ b/api_docs/security.devdocs.json @@ -1829,8 +1829,10 @@ "type": "Interface", "tags": [], "label": "APIKeys", - "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "description": [ + "\nInterface for managing API keys in Elasticsearch, including creation,\nvalidation, and invalidation of API keys,\nas well as checking the status of API key features." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -1846,7 +1848,7 @@ "signature": [ "() => Promise" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -1864,7 +1866,7 @@ "signature": [ "() => Promise" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [], @@ -1890,17 +1892,17 @@ }, ", createParams: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CreateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", "text": "CreateAPIKeyParams" }, ") => Promise<", "SecurityCreateApiKeyResponse", " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -1923,7 +1925,7 @@ }, "" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -1939,14 +1941,98 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.CreateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateAPIKeyParams", "text": "CreateAPIKeyParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.update", + "type": "Function", + "tags": [], + "label": "update", + "description": [ + "\nAttempts update an API key with the provided 'role_descriptors' and 'metadata'\n\nReturns `updated`, `true` if the update was successful, `false` if there was nothing to update\n\nUser needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys.\n" + ], + "signature": [ + "(request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + ", updateParams: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + }, + ") => Promise<", + "SecurityUpdateApiKeyResponse", + " | null>" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.APIKeys.update.$1", + "type": "Object", + "tags": [], + "label": "request", + "description": [ + "Request instance." + ], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "security", + "id": "def-server.APIKeys.update.$2", + "type": "CompoundType", + "tags": [], + "label": "updateParams", + "description": [ + "The params to edit an API key" + ], + "signature": [ + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.UpdateAPIKeyParams", + "text": "UpdateAPIKeyParams" + } + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -1972,17 +2058,33 @@ "section": "def-common.KibanaRequest", "text": "KibanaRequest" }, - ", createParams: Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>) => Promise<", + ", createParams: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.GrantAPIKeyResult", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.GrantAPIKeyResult", "text": "GrantAPIKeyResult" }, " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2005,7 +2107,7 @@ }, "" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2020,9 +2122,23 @@ "Create operation parameters." ], "signature": [ - "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }>" + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2042,15 +2158,15 @@ "signature": [ "(apiKeyPrams: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.ValidateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", "text": "ValidateAPIKeyParams" }, ") => Promise" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2065,14 +2181,14 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.ValidateAPIKeyParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ValidateAPIKeyParams", "text": "ValidateAPIKeyParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2100,23 +2216,23 @@ }, ", params: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" }, ") => Promise<", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeyResult", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", "text": "InvalidateAPIKeyResult" }, " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2139,7 +2255,7 @@ }, "" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2155,14 +2271,14 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2182,23 +2298,23 @@ "signature": [ "(params: ", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" }, ") => Promise<", { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeyResult", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeyResult", "text": "InvalidateAPIKeyResult" }, " | null>" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -2213,14 +2329,14 @@ ], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.InvalidateAPIKeysParams", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.InvalidateAPIKeysParams", "text": "InvalidateAPIKeysParams" } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "isRequired": true @@ -2984,10 +3100,10 @@ "description": [], "signature": [ { - "pluginId": "@kbn/security-plugin-types-server", - "scope": "server", - "docId": "kibKbnSecurityPluginTypesServerPluginApi", - "section": "def-server.APIKeys", + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.APIKeys", "text": "APIKeys" } ], @@ -3985,51 +4101,79 @@ }, { "parentPluginId": "security", - "id": "def-server.GrantAPIKeyResult", + "id": "def-server.CreateCrossClusterAPIKeyParams", "type": "Interface", "tags": [], - "label": "GrantAPIKeyResult", + "label": "CreateCrossClusterAPIKeyParams", "description": [], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.GrantAPIKeyResult.id", + "id": "def-server.CreateCrossClusterAPIKeyParams.type", "type": "string", "tags": [], - "label": "id", - "description": [ - "\nUnique id for this API key" + "label": "type", + "description": [], + "signature": [ + "\"cross_cluster\"" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.GrantAPIKeyResult.name", + "id": "def-server.CreateCrossClusterAPIKeyParams.expiration", "type": "string", "tags": [], - "label": "name", - "description": [ - "\nName for this API key" + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.GrantAPIKeyResult.api_key", + "id": "def-server.CreateCrossClusterAPIKeyParams.name", "type": "string", "tags": [], - "label": "api_key", - "description": [ - "\nGenerated API key" + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.CreateCrossClusterAPIKeyParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.CreateCrossClusterAPIKeyParams.access", + "type": "Object", + "tags": [], + "label": "access", + "description": [], + "signature": [ + "{ search?: { names: string[]; query?: unknown; field_security?: unknown; allow_restricted_indices?: boolean | undefined; }[] | undefined; replication?: { names: string[]; }[] | undefined; }" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -4038,78 +4182,395 @@ }, { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeyResult", + "id": "def-server.CreateRestAPIKeyParams", "type": "Interface", "tags": [], - "label": "InvalidateAPIKeyResult", - "description": [ - "\nThe return value when invalidating an API key in Elasticsearch." - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "label": "CreateRestAPIKeyParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeyResult.invalidated_api_keys", - "type": "Array", + "id": "def-server.CreateRestAPIKeyParams.type", + "type": "string", "tags": [], - "label": "invalidated_api_keys", - "description": [ - "\nThe IDs of the API keys that were invalidated as part of the request." - ], + "label": "type", + "description": [], "signature": [ - "string[]" + "\"rest\" | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeyResult.previously_invalidated_api_keys", - "type": "Array", + "id": "def-server.CreateRestAPIKeyParams.expiration", + "type": "string", "tags": [], - "label": "previously_invalidated_api_keys", - "description": [ - "\nThe IDs of the API keys that were already invalidated." - ], + "label": "expiration", + "description": [], "signature": [ - "string[]" + "string | undefined" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeyResult.error_count", - "type": "number", + "id": "def-server.CreateRestAPIKeyParams.name", + "type": "string", "tags": [], - "label": "error_count", - "description": [ - "\nThe number of errors that were encountered when invalidating the API keys." - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "security", - "id": "def-server.InvalidateAPIKeyResult.error_details", - "type": "Array", + "id": "def-server.CreateRestAPIKeyParams.role_descriptors", + "type": "Object", "tags": [], - "label": "error_details", - "description": [ - "\nDetails about these errors. This field is not present in the response when error_count is 0." - ], + "label": "role_descriptors", + "description": [], "signature": [ - "{ type?: string | undefined; reason?: string | undefined; caused_by?: { type?: string | undefined; reason?: string | undefined; } | undefined; }[] | undefined" + "{ [x: string]: { [key: string]: any; }; }" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false - } + }, + { + "parentPluginId": "security", + "id": "def-server.CreateRestAPIKeyParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams", + "type": "Interface", + "tags": [], + "label": "CreateRestAPIKeyWithKibanaPrivilegesParams", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"rest\" | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.expiration", + "type": "string", + "tags": [], + "label": "expiration", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [], + "signature": [ + "{ [key: string]: any; } | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams.kibana_role_descriptors", + "type": "Object", + "tags": [], + "label": "kibana_role_descriptors", + "description": [], + "signature": [ + "{ [x: string]: { elasticsearch: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.ElasticsearchPrivilegesType", + "text": "ElasticsearchPrivilegesType" + }, + " & { [key: string]: unknown; }; kibana: ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.KibanaPrivilegesType", + "text": "KibanaPrivilegesType" + }, + "; }; }" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.ElasticsearchPrivilegesType", + "type": "Interface", + "tags": [], + "label": "ElasticsearchPrivilegesType", + "description": [ + "\nType representing Elasticsearch specific portion of the role definition." + ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.ElasticsearchPrivilegesType.cluster", + "type": "Array", + "tags": [], + "label": "cluster", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.ElasticsearchPrivilegesType.remote_cluster", + "type": "Array", + "tags": [], + "label": "remote_cluster", + "description": [], + "signature": [ + "{ privileges: string[]; clusters: string[]; }[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.ElasticsearchPrivilegesType.indices", + "type": "Array", + "tags": [], + "label": "indices", + "description": [], + "signature": [ + "{ names: string[]; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; privileges: string[]; query?: string | undefined; allow_restricted_indices?: boolean | undefined; }[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.ElasticsearchPrivilegesType.remote_indices", + "type": "Array", + "tags": [], + "label": "remote_indices", + "description": [], + "signature": [ + "{ clusters: string[]; names: string[]; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; privileges: string[]; query?: string | undefined; allow_restricted_indices?: boolean | undefined; }[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.ElasticsearchPrivilegesType.run_as", + "type": "Array", + "tags": [], + "label": "run_as", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/roles/schema.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.GrantAPIKeyResult", + "type": "Interface", + "tags": [], + "label": "GrantAPIKeyResult", + "description": [], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.GrantAPIKeyResult.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "\nUnique id for this API key" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.GrantAPIKeyResult.name", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "\nName for this API key" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.GrantAPIKeyResult.api_key", + "type": "string", + "tags": [], + "label": "api_key", + "description": [ + "\nGenerated API key" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeyResult", + "type": "Interface", + "tags": [], + "label": "InvalidateAPIKeyResult", + "description": [ + "\nThe return value when invalidating an API key in Elasticsearch." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeyResult.invalidated_api_keys", + "type": "Array", + "tags": [], + "label": "invalidated_api_keys", + "description": [ + "\nThe IDs of the API keys that were invalidated as part of the request." + ], + "signature": [ + "string[]" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeyResult.previously_invalidated_api_keys", + "type": "Array", + "tags": [], + "label": "previously_invalidated_api_keys", + "description": [ + "\nThe IDs of the API keys that were already invalidated." + ], + "signature": [ + "string[]" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeyResult.error_count", + "type": "number", + "tags": [], + "label": "error_count", + "description": [ + "\nThe number of errors that were encountered when invalidating the API keys." + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "security", + "id": "def-server.InvalidateAPIKeyResult.error_details", + "type": "Array", + "tags": [], + "label": "error_details", + "description": [ + "\nDetails about these errors. This field is not present in the response when error_count is 0." + ], + "signature": [ + "{ type?: string | undefined; reason?: string | undefined; caused_by?: { type?: string | undefined; reason?: string | undefined; } | undefined; }[] | undefined" + ], + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", + "deprecated": false, + "trackAdoption": false + } ], "initialIsOpen": false }, @@ -4122,7 +4583,7 @@ "description": [ "\nRepresents the params for invalidating multiple API keys" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4132,11 +4593,13 @@ "type": "Array", "tags": [], "label": "ids", - "description": [], + "description": [ + "\nList of unique API key IDs" + ], "signature": [ "string[]" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -4734,7 +5197,7 @@ "description": [ "\nRepresents the parameters for validating API Key credentials." ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "children": [ @@ -4747,7 +5210,7 @@ "description": [ "\nUnique id for this API key" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false }, @@ -4760,7 +5223,7 @@ "description": [ "\nGenerated API Key (secret)" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false } @@ -5092,9 +5555,31 @@ "label": "CreateAPIKeyParams", "description": [], "signature": [ - "Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; role_descriptors: Record>; }> | Readonly<{ type?: \"rest\" | undefined; metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { name: string; kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }> | Readonly<{ metadata?: Readonly<{} & {}> | undefined; expiration?: string | undefined; } & { type: \"cross_cluster\"; name: string; access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }>" + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyParams", + "text": "CreateRestAPIKeyParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateRestAPIKeyWithKibanaPrivilegesParams", + "text": "CreateRestAPIKeyWithKibanaPrivilegesParams" + }, + " | ", + { + "pluginId": "@kbn/core-security-server", + "scope": "common", + "docId": "kibKbnCoreSecurityServerPluginApi", + "section": "def-common.CreateCrossClusterAPIKeyParams", + "text": "CreateCrossClusterAPIKeyParams" + } ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5111,67 +5596,7 @@ "signature": [ "SecurityCreateApiKeyResponse" ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-server.CreateCrossClusterAPIKeyParams", - "type": "Type", - "tags": [], - "label": "CreateCrossClusterAPIKeyParams", - "description": [], - "signature": [ - "{ readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly type: \"cross_cluster\"; readonly name: string; readonly access: Readonly<{ search?: Readonly<{ query?: any; field_security?: any; allow_restricted_indices?: boolean | undefined; } & { names: string[]; }>[] | undefined; replication?: Readonly<{} & { names: string[]; }>[] | undefined; } & {}>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-server.CreateRestAPIKeyParams", - "type": "Type", - "tags": [], - "label": "CreateRestAPIKeyParams", - "description": [], - "signature": [ - "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly role_descriptors: Record>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-server.CreateRestAPIKeyWithKibanaPrivilegesParams", - "type": "Type", - "tags": [], - "label": "CreateRestAPIKeyWithKibanaPrivilegesParams", - "description": [], - "signature": [ - "{ readonly type?: \"rest\" | undefined; readonly metadata?: Readonly<{} & {}> | undefined; readonly expiration?: string | undefined; readonly name: string; readonly kibana_role_descriptors: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]; elasticsearch: Readonly<{ cluster?: string[] | undefined; indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; run_as?: string[] | undefined; } & {}>; }>>; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "security", - "id": "def-server.ElasticsearchPrivilegesType", - "type": "Type", - "tags": [], - "label": "ElasticsearchPrivilegesType", - "description": [], - "signature": [ - "{ readonly cluster?: string[] | undefined; readonly indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; }>[] | undefined; readonly remote_cluster?: Readonly<{} & { privileges: string[]; clusters: string[]; }>[] | undefined; readonly remote_indices?: Readonly<{ query?: string | undefined; field_security?: Record<\"except\" | \"grant\", string[]> | undefined; allow_restricted_indices?: boolean | undefined; } & { names: string[]; privileges: string[]; clusters: string[]; }>[] | undefined; readonly run_as?: string[] | undefined; }" - ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "path": "packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5182,11 +5607,13 @@ "type": "Type", "tags": [], "label": "KibanaPrivilegesType", - "description": [], + "description": [ + "\nType representing Kibana specific portion of the role definition." + ], "signature": [ - "Readonly<{ base?: string[] | undefined; feature?: Record | undefined; } & { spaces: string[] | \"*\"[]; }>[]" + "{ spaces: string[]; base?: string[] | undefined; feature?: Record | undefined; }[]" ], - "path": "x-pack/packages/security/plugin_types_server/src/authorization/role_schema.ts", + "path": "packages/core/security/core-security-server/src/roles/schema.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -5316,29 +5743,9 @@ "plugin": "ml", "path": "x-pack/plugins/ml/server/routes/annotations.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/request_context_factory.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts" } ] }, @@ -5491,30 +5898,6 @@ "plugin": "enterpriseSearch", "path": "x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts" - }, { "plugin": "serverlessSearch", "path": "x-pack/plugins/serverless_search/server/routes/api_key_routes.ts" @@ -5614,30 +5997,6 @@ { "plugin": "fleet", "path": "x-pack/plugins/fleet/server/routes/setup/handlers.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/list.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/actions/state.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/base_validator.test.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts" } ] }, diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 426eb872fc138..ee72de420f716 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 415 | 0 | 206 | 1 | +| 438 | 0 | 222 | 1 | ## Client diff --git a/api_docs/security_solution.devdocs.json b/api_docs/security_solution.devdocs.json index e07a045ba7722..b5d8334c5d5b0 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -485,7 +485,7 @@ "\nExperimental flag needed to enable the link" ], "signature": [ - "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"responseActionScanEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutDisabled\" | \"securitySolutionNotesEnabled\" | \"entityAlertPreviewEnabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"AIAssistantOnRuleCreationFormEnabled\" | \"disableTimelineSaveTour\" | \"alertSuppressionForEsqlRuleEnabled\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForMachineLearningRuleEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"aiAssistantFlyoutMode\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | \"manualRuleRunEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | undefined" + "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsSentinelOneKillProcessEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"responseActionScanEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutDisabled\" | \"securitySolutionNotesEnabled\" | \"entityAlertPreviewEnabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"AIAssistantOnRuleCreationFormEnabled\" | \"disableTimelineSaveTour\" | \"alertSuppressionForEsqlRuleEnabled\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForMachineLearningRuleEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | \"manualRuleRunEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -565,7 +565,7 @@ "\nExperimental flag needed to disable the link. Opposite of experimentalKey" ], "signature": [ - "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"responseActionScanEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutDisabled\" | \"securitySolutionNotesEnabled\" | \"entityAlertPreviewEnabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"AIAssistantOnRuleCreationFormEnabled\" | \"disableTimelineSaveTour\" | \"alertSuppressionForEsqlRuleEnabled\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForMachineLearningRuleEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"aiAssistantFlyoutMode\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | \"manualRuleRunEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | undefined" + "\"assistantKnowledgeBaseByDefault\" | \"assistantModelEvaluation\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"extendedRuleExecutionLoggingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"automatedProcessActionsEnabled\" | \"responseActionsSentinelOneV1Enabled\" | \"responseActionsSentinelOneV2Enabled\" | \"responseActionsSentinelOneGetFileEnabled\" | \"responseActionsSentinelOneKillProcessEnabled\" | \"responseActionsCrowdstrikeManualHostIsolationEnabled\" | \"responseActionScanEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutDisabled\" | \"securitySolutionNotesEnabled\" | \"entityAlertPreviewEnabled\" | \"newUserDetailsFlyoutManagedUser\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"AIAssistantOnRuleCreationFormEnabled\" | \"disableTimelineSaveTour\" | \"alertSuppressionForEsqlRuleEnabled\" | \"riskEnginePrivilegesRouteEnabled\" | \"alertSuppressionForMachineLearningRuleEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"crowdstrikeDataInAnalyzerEnabled\" | \"jamfDataInAnalyzerEnabled\" | \"timelineEsqlTabDisabled\" | \"unifiedComponentsInTimelineEnabled\" | \"analyzerDatePickersAndSourcererDisabled\" | \"prebuiltRulesCustomizationEnabled\" | \"malwareOnWriteScanOptionAvailable\" | \"unifiedManifestEnabled\" | \"valueListItemsModalEnabled\" | \"bulkCustomHighlightedFieldsEnabled\" | \"manualRuleRunEnabled\" | \"filterProcessDescendantsForEventFiltersEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -1964,7 +1964,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly responseActionScanEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutDisabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly AIAssistantOnRuleCreationFormEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly alertSuppressionForEsqlRuleEnabled: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForMachineLearningRuleEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; readonly manualRuleRunEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; }" + "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly responseActionScanEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutDisabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly AIAssistantOnRuleCreationFormEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly alertSuppressionForEsqlRuleEnabled: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForMachineLearningRuleEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; readonly manualRuleRunEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/types.ts", "deprecated": false, @@ -3071,7 +3071,7 @@ "\nThe security solution generic experimental features" ], "signature": [ - "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly responseActionScanEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutDisabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly AIAssistantOnRuleCreationFormEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly alertSuppressionForEsqlRuleEnabled: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForMachineLearningRuleEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; readonly manualRuleRunEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; }" + "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly responseActionScanEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutDisabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly AIAssistantOnRuleCreationFormEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly alertSuppressionForEsqlRuleEnabled: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForMachineLearningRuleEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; readonly manualRuleRunEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/server/plugin_contract.ts", "deprecated": false, @@ -3247,7 +3247,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly responseActionScanEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutDisabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly AIAssistantOnRuleCreationFormEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly alertSuppressionForEsqlRuleEnabled: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForMachineLearningRuleEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly aiAssistantFlyoutMode: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; readonly manualRuleRunEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; }" + "{ readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly automatedProcessActionsEnabled: boolean; readonly responseActionsSentinelOneV1Enabled: boolean; readonly responseActionsSentinelOneV2Enabled: boolean; readonly responseActionsSentinelOneGetFileEnabled: boolean; readonly responseActionsSentinelOneKillProcessEnabled: boolean; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: boolean; readonly responseActionScanEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutDisabled: boolean; readonly securitySolutionNotesEnabled: boolean; readonly entityAlertPreviewEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantKnowledgeBaseByDefault: boolean; readonly newUserDetailsFlyoutManagedUser: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly AIAssistantOnRuleCreationFormEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly alertSuppressionForEsqlRuleEnabled: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly alertSuppressionForMachineLearningRuleEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly crowdstrikeDataInAnalyzerEnabled: boolean; readonly jamfDataInAnalyzerEnabled: boolean; readonly timelineEsqlTabDisabled: boolean; readonly unifiedComponentsInTimelineEnabled: boolean; readonly analyzerDatePickersAndSourcererDisabled: boolean; readonly prebuiltRulesCustomizationEnabled: boolean; readonly malwareOnWriteScanOptionAvailable: boolean; readonly unifiedManifestEnabled: boolean; readonly valueListItemsModalEnabled: boolean; readonly bulkCustomHighlightedFieldsEnabled: boolean; readonly manualRuleRunEnabled: boolean; readonly filterProcessDescendantsForEventFiltersEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, @@ -3313,7 +3313,7 @@ "\nA list of allowed values that can be used in `xpack.securitySolution.enableExperimental`.\nThis object is then used to validate and parse the value entered." ], "signature": [ - "{ readonly excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly extendedRuleExecutionLoggingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionsEnabled: true; readonly endpointResponseActionsEnabled: true; readonly responseActionUploadEnabled: true; readonly automatedProcessActionsEnabled: true; readonly responseActionsSentinelOneV1Enabled: true; readonly responseActionsSentinelOneV2Enabled: true; readonly responseActionsSentinelOneGetFileEnabled: true; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: true; readonly responseActionScanEnabled: false; readonly alertsPageChartsEnabled: true; readonly alertTypeEnabled: false; readonly expandableFlyoutDisabled: false; readonly securitySolutionNotesEnabled: false; readonly entityAlertPreviewEnabled: false; readonly assistantModelEvaluation: false; readonly assistantKnowledgeBaseByDefault: false; readonly newUserDetailsFlyoutManagedUser: false; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly AIAssistantOnRuleCreationFormEnabled: false; readonly disableTimelineSaveTour: false; readonly alertSuppressionForEsqlRuleEnabled: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly alertSuppressionForMachineLearningRuleEnabled: false; readonly sentinelOneDataInAnalyzerEnabled: true; readonly sentinelOneManualHostActionsEnabled: true; readonly crowdstrikeDataInAnalyzerEnabled: true; readonly jamfDataInAnalyzerEnabled: false; readonly timelineEsqlTabDisabled: false; readonly unifiedComponentsInTimelineEnabled: false; readonly analyzerDatePickersAndSourcererDisabled: false; readonly prebuiltRulesCustomizationEnabled: false; readonly malwareOnWriteScanOptionAvailable: true; readonly unifiedManifestEnabled: true; readonly aiAssistantFlyoutMode: true; readonly valueListItemsModalEnabled: true; readonly bulkCustomHighlightedFieldsEnabled: false; readonly manualRuleRunEnabled: false; readonly filterProcessDescendantsForEventFiltersEnabled: false; }" + "{ readonly excludePoliciesInFilterEnabled: false; readonly kubernetesEnabled: true; readonly donutChartEmbeddablesEnabled: false; readonly previewTelemetryUrlEnabled: false; readonly extendedRuleExecutionLoggingEnabled: false; readonly socTrendsEnabled: false; readonly responseActionsEnabled: true; readonly endpointResponseActionsEnabled: true; readonly responseActionUploadEnabled: true; readonly automatedProcessActionsEnabled: true; readonly responseActionsSentinelOneV1Enabled: true; readonly responseActionsSentinelOneV2Enabled: true; readonly responseActionsSentinelOneGetFileEnabled: true; readonly responseActionsSentinelOneKillProcessEnabled: false; readonly responseActionsCrowdstrikeManualHostIsolationEnabled: true; readonly responseActionScanEnabled: false; readonly alertsPageChartsEnabled: true; readonly alertTypeEnabled: false; readonly expandableFlyoutDisabled: false; readonly securitySolutionNotesEnabled: false; readonly entityAlertPreviewEnabled: false; readonly assistantModelEvaluation: false; readonly assistantKnowledgeBaseByDefault: false; readonly newUserDetailsFlyoutManagedUser: false; readonly riskScoringPersistence: true; readonly riskScoringRoutesEnabled: true; readonly esqlRulesDisabled: false; readonly protectionUpdatesEnabled: true; readonly AIAssistantOnRuleCreationFormEnabled: false; readonly disableTimelineSaveTour: false; readonly alertSuppressionForEsqlRuleEnabled: false; readonly riskEnginePrivilegesRouteEnabled: true; readonly alertSuppressionForMachineLearningRuleEnabled: false; readonly sentinelOneDataInAnalyzerEnabled: true; readonly sentinelOneManualHostActionsEnabled: true; readonly crowdstrikeDataInAnalyzerEnabled: true; readonly jamfDataInAnalyzerEnabled: false; readonly timelineEsqlTabDisabled: false; readonly unifiedComponentsInTimelineEnabled: false; readonly analyzerDatePickersAndSourcererDisabled: false; readonly prebuiltRulesCustomizationEnabled: false; readonly malwareOnWriteScanOptionAvailable: true; readonly unifiedManifestEnabled: true; readonly valueListItemsModalEnabled: true; readonly bulkCustomHighlightedFieldsEnabled: false; readonly manualRuleRunEnabled: false; readonly filterProcessDescendantsForEventFiltersEnabled: false; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 2beb77b8f021a..b83ae20ff02b2 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index 6f910f41ef9fa..de3bb64f52ced 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index c45b80b05022b..3242828d62e87 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 5edadd4df8584..a0de5bcc4cf91 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index a0d2eb5ba6340..e64f97973a9dc 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index 5caf420464062..09f2c7edcf5da 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 780c2b3b45e42..f7b44f92491a4 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 3131c5c2c6108..264403c94db1f 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 1bceb41948c73..f2cb7a913790a 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index b12b6a9ec84e1..530b22186bb50 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index e47eb24a3ffce..a475fdea0ff3c 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 7bd81f0d77fe7..49ee40840fb95 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 0d8eeb21ffb4b..9ef3fb6e5248e 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index cafa9df73c7d7..bcf328cc6ca79 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index bd107e8ace5ae..af4a57ecedc3b 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 156c0703d5d00..4b678c60a7a40 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index f998b3df27e23..38b3f69b9ca09 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index c930712b12bcf..cfbad9700d4d9 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index ef23eb49129cc..5d3ce3746744c 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 99e3b0e874c36..638e5226e0a35 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index 2a2604e67dd17..16ce978528dd6 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index e4b742cbd7d1f..e041317a6070c 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 882b2c6be2044..c090c5755ab42 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 5aa9989c99e79..c68df9b322654 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index 1caa3e13cf3c6..95c4364f43a22 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index e523bb984eb86..4f4a60ac31a60 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index bf5e4d8873990..47438fb7a9b30 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 9be2c3796bffd..c0d5599e462e5 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 41ab261a2f6a1..d7c9168a273e7 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; This plugin visualizes data from Heartbeat, and integrates with other Observability solutions. -Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) for questions regarding this plugin. +Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 57e7235cc89b4..e36131ead0d63 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 57af900f407c7..5a83dde70882d 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 04268f31c0558..c1a224812e6f9 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index 00204bf4434c2..e66d2e7f0cf1b 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 0524cbaa8be37..ba1bfdeb89f5e 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index 88bc88e7f03a2..cec157b72cc1d 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 90f824637708d..9478bfc37db41 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 986e5117524d0..8d3505f45293d 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index f89789624e87c..5fcdd9938c1be 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 3f26a6dc228f4..bc6b238b14e3d 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index c32fdb2696281..617429675c83a 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 85f6705d4d4cc..aff385709f8b3 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 62f1b17b448d9..8165741192e1c 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 210f5acd288f1..d7212a6bd8a8f 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-07-09 +date: 2024-07-11 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/catalog-info.yaml b/catalog-info.yaml index aae8fc1af81d8..4af2698ca6cca 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -97,34 +97,6 @@ spec: # yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json apiVersion: backstage.io/v1alpha1 kind: Resource -metadata: - name: buildkite-pipeline-kibana-kme-test -spec: - implementation: - apiVersion: buildkite.elastic.dev/v1 - kind: Pipeline - metadata: - description: Temporary pipeline for testing Kibana KME work - name: kibana-kme-test - spec: - pipeline_file: .buildkite/scripts/pipelines/pull_request/pipeline.sh - provider_settings: - build_branches: false - build_pull_requests: true - publish_commit_status: false - trigger_mode: none - repository: elastic/kibana - teams: - kibana-operations: - access_level: MANAGE_BUILD_AND_READ - everyone: - access_level: READ_ONLY - owner: group:kibana-operations - type: buildkite-pipeline ---- -# yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json -apiVersion: backstage.io/v1alpha1 -kind: Resource metadata: name: buildkite-pipeline-kibana-sonarqube description: Run a SonarQube scan diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 6d42b25d4bce4..c5a1dd2798fbb 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -10,6 +10,7 @@ Review important information about the {kib} 8.x releases. +* <> * <> * <> * <> @@ -69,6 +70,21 @@ Review important information about the {kib} 8.x releases. -- +[[release-notes-8.14.3]] +== {kib} 8.14.3 + +The 8.14.3 release includes the following bug fixes. + +[float] +[[fixes-v8.14.3]] +=== Bug Fixes +Dashboard:: +* Fixes controls getting overwritten on navigation ({kibana-pull}187509[#187509]). +Elastic Security:: +For the Elastic Security 8.14.3 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Logs:: +* Fixes log entry fly-out when response is slow ({kibana-pull}187303[#187303]). + [[release-notes-8.14.2]] == {kib} 8.14.2 diff --git a/docs/api/data-views/create.asciidoc b/docs/api/data-views/create.asciidoc index 2164983db11ea..32372639d2dbf 100644 --- a/docs/api/data-views/create.asciidoc +++ b/docs/api/data-views/create.asciidoc @@ -231,10 +231,13 @@ The API returns the {data-source} object: .Properties of the fieldAttrs[fieldName] objects: [%collapsible%open] ===== -`customLabel`::: +`customLabel`:: (Optional, string) Custom label for the field. -`count`::: +`customDescription`:: +(Optional, string) Custom description for the field. Max length is 300 characters. + +`count`:: (Optional, number) Popularity count for the field. ===== diff --git a/docs/api/data-views/update-fields.asciidoc b/docs/api/data-views/update-fields.asciidoc index 9b0b044238f36..0feacccb81863 100644 --- a/docs/api/data-views/update-fields.asciidoc +++ b/docs/api/data-views/update-fields.asciidoc @@ -5,7 +5,7 @@ ++++ experimental[] Update fields presentation metadata, such as `count`, -`customLabel`, and `format`. You can update multiple fields in one request. Updates +`customLabel`, `customDescription`, and `format`. You can update multiple fields in one request. Updates are merged with persisted metadata. To remove existing metadata, specify `null` as the value. @@ -119,7 +119,8 @@ $ curl -X POST api/data_views/data_view/my-view/fields "customLabel": "Foo" }, "bar": { - "customLabel": "Bar" + "customLabel": "Bar", + "customDescription": "Bar Custom description" } } } diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index e4f161ac8f4e5..0f20d331118cc 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -102,6 +102,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |Embeddables are React components that manage their own state, can be serialized and deserialized, and return an API that can be used to interact with them imperatively. +|{kib-repo}blob/{branch}/src/plugins/esql/README.md[esql] +|The editor accepts the following properties: + + |{kib-repo}blob/{branch}/src/plugins/esql_datagrid/README.md[esqlDataGrid] |Contains a Discover-like table specifically for ES|QL queries: @@ -328,10 +332,6 @@ generating deep links to other apps using locators, and creating short URLs. |This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry). -|{kib-repo}blob/{branch}/src/plugins/text_based_languages/README.md[textBasedLanguages] -|The editor accepts the following properties: - - |<> |UI Actions plugins provides API to manage *triggers* and *actions*. diff --git a/docs/discover/images/esql-full-query.png b/docs/discover/images/esql-full-query.png index 1d4a37af23a60..e4f5faeef3cf7 100644 Binary files a/docs/discover/images/esql-full-query.png and b/docs/discover/images/esql-full-query.png differ diff --git a/docs/discover/images/esql-limit.png b/docs/discover/images/esql-limit.png index dbc9edc3cdc13..b03ecdcc091e6 100644 Binary files a/docs/discover/images/esql-limit.png and b/docs/discover/images/esql-limit.png differ diff --git a/docs/discover/images/esql-machine-os-ram.png b/docs/discover/images/esql-machine-os-ram.png index 2c936cecb9498..ad46d88b219ff 100644 Binary files a/docs/discover/images/esql-machine-os-ram.png and b/docs/discover/images/esql-machine-os-ram.png differ diff --git a/docs/discover/try-esql.asciidoc b/docs/discover/try-esql.asciidoc index 32718d87c955a..53862be75f010 100644 --- a/docs/discover/try-esql.asciidoc +++ b/docs/discover/try-esql.asciidoc @@ -78,7 +78,7 @@ FROM kibana_sample_data_logs . Click **▶Run**. + [role="screenshot"] -image:images/esql-full-query.png[] +image:images/esql-full-query.png[An image of the full query result] + . Click **Save** to save the query and visualization to a dashboard. diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 98886aedd5535..73789c750e015 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -43,8 +43,11 @@ Change the settings that apply only to {kib} spaces. [[auto-complete-use-time-tange]]`autocomplete:useTimeRange`:: When disabled, autocompletes the suggestions from your data set instead of the time range. +[[bfetch-disable]]`bfetch:disable`:: +deprecated:[8.15.0] When disabled, search requests from Kibana will be made in individual HTTP requests rather than bundled together. + [[bfetch-disable-compression]]`bfetch:disableCompression`:: -When disabled, allows you to debug individual requests, but increases the response size. +deprecated:[8.15.0] When disabled, allows you to debug individual requests, but increases the response size. [[csv-quotevalues]]`csv:quoteValues`:: Set this property to `true` to quote exported values. diff --git a/docs/management/connectors/action-types/openai.asciidoc b/docs/management/connectors/action-types/openai.asciidoc index 49033b92cd740..968f5a1e30f3a 100644 --- a/docs/management/connectors/action-types/openai.asciidoc +++ b/docs/management/connectors/action-types/openai.asciidoc @@ -4,8 +4,8 @@ OpenAI ++++ :frontmatter-description: Add a connector that can send requests to an OpenAI provider. -:frontmatter-tags-products: [kibana] -:frontmatter-tags-content-type: [how-to] +:frontmatter-tags-products: [kibana] +:frontmatter-tags-content-type: [how-to] :frontmatter-tags-user-goals: [configure] @@ -21,6 +21,17 @@ You can create connectors in *{stack-manage-app} > {connectors-ui}*. For exampl image::management/connectors/images/gen-ai-connector.png[OpenAI connector] // NOTE: This is an autogenerated screenshot. Do not edit it directly. +[IMPORTANT] +==== +Elastic provides no official support for connecting to the Azure OpenAI service through a proxy. +However if you must use a proxy, +ensure that the proxy supports streaming and is SSE-compatible. +Elastic will only parse streamed responses. + +To validate that your connectivity problems are caused by using a proxy, +you can attempt to set up the connector and access the Azure OpenAI service without using a proxy. +==== + [float] [[openai-connector-configuration]] ==== Connector configuration @@ -46,7 +57,7 @@ image::management/connectors/images/gen-ai-params-test.png[OpenAI params test] The OpenAI actions have the following configuration properties. -Body:: A JSON payload sent to the OpenAI API URL. For example: +Body:: A JSON payload sent to the OpenAI API URL. For example: + [source,text] -- diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index 050aee9d2e972..60d0494de9c71 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -80,6 +80,10 @@ "id": "kibDevDocsSavedObjectsIntro", "label": "Saved objects" }, + { + "id": "kibDevDocsEncryptedSavedObjectsIntro", + "label": "Encrypted Saved objects" + }, { "id": "kibDevDocsPersistableStateIntro" }, @@ -614,4 +618,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/package.json b/package.json index e9a8d26a23c8e..97a82c8da8e93 100644 --- a/package.json +++ b/package.json @@ -454,6 +454,7 @@ "@kbn/es-ui-shared-plugin": "link:src/plugins/es_ui_shared", "@kbn/eso-model-version-example": "link:examples/eso_model_version_example", "@kbn/eso-plugin": "link:x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin", + "@kbn/esql": "link:src/plugins/esql", "@kbn/esql-ast": "link:packages/kbn-esql-ast", "@kbn/esql-ast-inspector-plugin": "link:examples/esql_ast_inspector", "@kbn/esql-datagrid": "link:src/plugins/esql_datagrid", @@ -674,6 +675,7 @@ "@kbn/react-kibana-context-styled": "link:packages/react/kibana_context/styled", "@kbn/react-kibana-context-theme": "link:packages/react/kibana_context/theme", "@kbn/react-kibana-mount": "link:packages/react/kibana_mount", + "@kbn/recently-accessed": "link:packages/kbn-recently-accessed", "@kbn/remote-clusters-plugin": "link:x-pack/plugins/remote_clusters", "@kbn/rendering-plugin": "link:test/plugin_functional/plugins/rendering_plugin", "@kbn/repo-info": "link:packages/kbn-repo-info", @@ -864,7 +866,6 @@ "@kbn/test-feature-usage-plugin": "link:x-pack/test/licensing_plugin/plugins/test_feature_usage", "@kbn/testing-embedded-lens-plugin": "link:x-pack/examples/testing_embedded_lens", "@kbn/text-based-editor": "link:packages/kbn-text-based-editor", - "@kbn/text-based-languages": "link:src/plugins/text_based_languages", "@kbn/third-party-lens-navigation-prompt-plugin": "link:x-pack/examples/third_party_lens_navigation_prompt", "@kbn/third-party-vis-lens-example-plugin": "link:x-pack/examples/third_party_vis_lens_example", "@kbn/threat-intelligence-plugin": "link:x-pack/plugins/threat_intelligence", @@ -947,6 +948,7 @@ "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mapbox/mapbox-gl-supported": "2.0.1", "@mapbox/vector-tile": "1.3.1", + "@mswjs/http-middleware": "^0.10.1", "@opentelemetry/api": "^1.1.0", "@opentelemetry/api-metrics": "^0.31.0", "@opentelemetry/exporter-metrics-otlp-grpc": "^0.34.0", @@ -1033,6 +1035,7 @@ "extract-zip": "^2.0.1", "fast-deep-equal": "^3.1.1", "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.12", "fflate": "^0.6.9", "file-saver": "^1.3.8", "flat": "5", @@ -1062,7 +1065,6 @@ "joi": "^17.13.3", "joi-to-json": "^4.3.0", "jquery": "^3.5.0", - "js-levenshtein": "^1.1.6", "js-search": "^1.4.3", "js-sha256": "^0.9.0", "js-yaml": "^3.14.1", @@ -1482,7 +1484,6 @@ "@types/inquirer": "^7.3.1", "@types/jest": "^29.5.3", "@types/jquery": "^3.3.31", - "@types/js-levenshtein": "^1.1.0", "@types/js-search": "^1.4.0", "@types/js-yaml": "^3.11.1", "@types/jsdom": "^20.0.1", diff --git a/packages/content-management/table_list_view/src/table_list_view.tsx b/packages/content-management/table_list_view/src/table_list_view.tsx index a5150c959e4f5..06c2566936d5a 100644 --- a/packages/content-management/table_list_view/src/table_list_view.tsx +++ b/packages/content-management/table_list_view/src/table_list_view.tsx @@ -38,6 +38,7 @@ export type TableListViewProps & { title: string; description?: string; @@ -75,6 +76,7 @@ export const TableListView = ({ additionalRightSideActions, withoutPageTemplateWrapper, createdByEnabled, + recentlyAccessed, }: TableListViewProps) => { const PageTemplate = withoutPageTemplateWrapper ? (React.Fragment as unknown as typeof KibanaPageTemplate) @@ -124,6 +126,7 @@ export const TableListView = ({ onFetchSuccess={onFetchSuccess} setPageDataTestSubject={setPageDataTestSubject} createdByEnabled={createdByEnabled} + recentlyAccessed={recentlyAccessed} /> diff --git a/packages/content-management/table_list_view_table/src/components/table.tsx b/packages/content-management/table_list_view_table/src/components/table.tsx index dc3061e1e4802..058c12eba5bf3 100644 --- a/packages/content-management/table_list_view_table/src/components/table.tsx +++ b/packages/content-management/table_list_view_table/src/components/table.tsx @@ -13,7 +13,6 @@ import { EuiButton, EuiInMemoryTable, CriteriaWithPagination, - PropertySort, SearchFilterConfig, Direction, Query, @@ -59,6 +58,7 @@ interface Props extends State, TagManageme tableCaption: string; tableColumns: Array>; hasUpdatedAtMetadata: boolean; + hasRecentlyAccessedMetadata: boolean; deleteItems: TableListViewTableProps['deleteItems']; tableItemsRowActions: TableItemsRowActions; renderCreateButton: () => React.ReactElement | undefined; @@ -81,6 +81,7 @@ export function Table({ tableSort, tableFilter, hasUpdatedAtMetadata, + hasRecentlyAccessedMetadata, entityName, entityNamePlural, tagsToTableItemMap, @@ -174,12 +175,13 @@ export function Table({ ); }, }; - }, [hasUpdatedAtMetadata, onSortChange, tableSort]); + }, [hasUpdatedAtMetadata, onSortChange, tableSort, hasRecentlyAccessedMetadata]); const tagFilterPanel = useMemo(() => { if (!isTaggingEnabled()) return null; @@ -278,6 +280,11 @@ export function Table({ return { allUsers: Array.from(users), showNoUserOption: _showNoUserOption }; }, [createdByEnabled, items]); + const sorting = + tableSort.field === 'accessedAt' // "accessedAt" is a special case with a custom sorting + ? true // by passing "true" we disable the EuiInMemoryTable sorting and handle it ourselves, but sorting is still enabled + : { sort: tableSort }; + return ( ({ selection={selection} search={search} executeQueryOptions={{ enabled: false }} - sorting={tableSort ? { sort: tableSort as PropertySort } : undefined} + sorting={sorting} onChange={onTableChange} data-test-subj="itemsInMemTable" rowHeader="attributes.title" diff --git a/packages/content-management/table_list_view_table/src/components/table_sort_select.test.tsx b/packages/content-management/table_list_view_table/src/components/table_sort_select.test.tsx new file mode 100644 index 0000000000000..e2c62a46c0e71 --- /dev/null +++ b/packages/content-management/table_list_view_table/src/components/table_sort_select.test.tsx @@ -0,0 +1,64 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { sortByRecentlyAccessed } from './table_sort_select'; +import { UserContentCommonSchema } from '@kbn/content-management-table-list-view-common'; + +describe('sortByRecentlyAccessed', () => { + const items: UserContentCommonSchema[] = [ + { + id: 'item-1', + type: 'dashboard', + updatedAt: '2020-01-01T00:00:00Z', + attributes: { + title: 'Item 1', + }, + references: [], + }, + { + id: 'item-2', + type: 'dashboard', + updatedAt: '2020-01-02T00:00:00Z', + attributes: { + title: 'Item 2', + }, + createdBy: 'u_1', + references: [], + }, + { + id: 'item-3', + type: 'dashboard', + updatedAt: '2020-01-03T00:00:00Z', + attributes: { + title: 'Item 3', + }, + createdBy: 'u_2', + references: [], + }, + { + id: 'item-4', + type: 'dashboard', + updatedAt: '2020-01-04T00:00:00Z', + attributes: { + title: 'Item 4', + }, + references: [], + managed: true, + }, + ]; + + test('sort by last updated', () => { + const sortedItems = sortByRecentlyAccessed(items, []); + expect(sortedItems.map((item) => item.id)).toEqual(['item-4', 'item-3', 'item-2', 'item-1']); + }); + + test('pulls recently accessed to the top', () => { + const sortedItems = sortByRecentlyAccessed(items, [{ id: 'item-1' }, { id: 'item-2' }]); + expect(sortedItems.map((item) => item.id)).toEqual(['item-1', 'item-2', 'item-4', 'item-3']); + }); +}); diff --git a/packages/content-management/table_list_view_table/src/components/table_sort_select.tsx b/packages/content-management/table_list_view_table/src/components/table_sort_select.tsx index c9e06a29e9c8c..1b5d9080f8205 100644 --- a/packages/content-management/table_list_view_table/src/components/table_sort_select.tsx +++ b/packages/content-management/table_list_view_table/src/components/table_sort_select.tsx @@ -16,8 +16,10 @@ import { Direction, EuiText, useEuiTheme, + EuiIconTip, } from '@elastic/eui'; import { css } from '@emotion/react'; +import { UserContentCommonSchema } from '@kbn/content-management-table-list-view-common'; import { State } from '../table_list_view_table'; @@ -26,9 +28,15 @@ type SortItem = EuiSelectableOption & { direction: Direction; }; -export type SortColumnField = 'updatedAt' | 'attributes.title'; +export type SortColumnField = 'updatedAt' | 'attributes.title' | 'accessedAt'; const i18nText = { + accessedDescSort: i18n.translate( + 'contentManagement.tableList.listing.tableSortSelect.recentlyAccessedLabel', + { + defaultMessage: 'Recently viewed', + } + ), nameAscSort: i18n.translate('contentManagement.tableList.listing.tableSortSelect.nameAscLabel', { defaultMessage: 'Name A-Z', }), @@ -57,11 +65,17 @@ const i18nText = { interface Props { hasUpdatedAtMetadata: boolean; + hasRecentlyAccessedMetadata: boolean; tableSort: State['tableSort']; onChange?: (column: SortColumnField, direction: Direction) => void; } -export function TableSortSelect({ tableSort, hasUpdatedAtMetadata, onChange }: Props) { +export function TableSortSelect({ + tableSort, + hasUpdatedAtMetadata, + hasRecentlyAccessedMetadata, + onChange, +}: Props) { const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -81,6 +95,40 @@ export function TableSortSelect({ tableSort, hasUpdatedAtMetadata, onChange }: P }, ]; + if (hasRecentlyAccessedMetadata) { + opts = [ + { + label: i18nText.accessedDescSort, + column: + 'accessedAt' /* nonexistent field, used to identify this custom type of sorting */, + direction: 'desc', + append: ( + + ), + }, + ...opts, + ]; + } + if (hasUpdatedAtMetadata) { opts = opts.concat([ { @@ -100,6 +148,7 @@ export function TableSortSelect({ tableSort, hasUpdatedAtMetadata, onChange }: P return opts; }); + const selectedOptionLabel = options.find(({ checked }) => checked === 'on')?.label ?? ''; const panelHeaderCSS = css` @@ -165,8 +214,11 @@ export function TableSortSelect({ tableSort, hasUpdatedAtMetadata, onChange }: P <> {i18nText.headerSort} - singleSelection - aria-label="some aria label" + singleSelection={'always'} + aria-label={i18n.translate( + 'contentManagement.tableList.listing.tableSortSelect.sortingOptionsAriaLabel', + { defaultMessage: 'Sorting options' } + )} options={options} onChange={onSelectChange} data-test-subj="sortSelect" @@ -214,3 +266,25 @@ export function saveSorting( /* empty */ } } + +/** + * Default custom sorting for the table when recently accessed info is available + * Sorts by recently accessed list first and the by lastUpdatedAt + */ +export function sortByRecentlyAccessed( + items: T[], + recentlyAccessed: Array<{ id: string }> +) { + const recentlyAccessedMap = new Map(recentlyAccessed.map((item, index) => [item.id, index])); + return [...items].sort((a, b) => { + if (recentlyAccessedMap.has(a.id) && recentlyAccessedMap.has(b.id)) { + return recentlyAccessedMap.get(a.id)! - recentlyAccessedMap.get(b.id)!; + } else if (recentlyAccessedMap.has(a.id)) { + return -1; + } else if (recentlyAccessedMap.has(b.id)) { + return 1; + } else { + return a.updatedAt > b.updatedAt ? -1 : 1; + } + }); +} diff --git a/packages/content-management/table_list_view_table/src/reducer.tsx b/packages/content-management/table_list_view_table/src/reducer.tsx index b4cf3691f9d75..d239fad38c724 100644 --- a/packages/content-management/table_list_view_table/src/reducer.tsx +++ b/packages/content-management/table_list_view_table/src/reducer.tsx @@ -33,11 +33,18 @@ export function getReducer() { // Only change the table sort if it hasn't been changed already. // For example if its state comes from the URL, we don't want to override it here. - if (hasUpdatedAtMetadata && !state.sortColumnChanged) { - tableSort = { - field: 'updatedAt' as const, - direction: 'desc' as const, - }; + if (!state.sortColumnChanged) { + if (state.hasRecentlyAccessedMetadata) { + tableSort = { + field: 'accessedAt' as const, + direction: 'desc' as const, + }; + } else if (hasUpdatedAtMetadata) { + tableSort = { + field: 'updatedAt' as const, + direction: 'desc' as const, + }; + } } } diff --git a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx index c874747799480..e56322099d5ff 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx @@ -653,6 +653,91 @@ describe('TableListView', () => { }); }); + describe('column sorting with recently accessed', () => { + const setupColumnSorting = registerTestBed( + WithServices(TableListViewTable, { + TagList: getTagList({ references: [] }), + }), + { + defaultProps: { + ...requiredProps, + recentlyAccessed: { get: () => [{ id: '123', link: '', label: '' }] }, + }, + memoryRouter: { wrapComponent: true }, + } + ); + + const hits: UserContentCommonSchema[] = [ + { + id: '123', + updatedAt: twoDaysAgo.toISOString(), // first asc, last desc + type: 'dashboard', + attributes: { + title: 'z-foo', // first desc, last asc + }, + references: [{ id: 'id-tag-1', name: 'tag-1', type: 'tag' }], + }, + { + id: '456', + updatedAt: yesterday.toISOString(), // first desc, last asc + type: 'dashboard', + attributes: { + title: 'a-foo', // first asc, last desc + }, + references: [], + }, + ]; + + test('should initially sort by "Recently Accessed"', async () => { + let testBed: TestBed; + + await act(async () => { + testBed = await setupColumnSorting({ + findItems: jest.fn().mockResolvedValue({ total: hits.length, hits }), + }); + }); + + const { component, table } = testBed!; + component.update(); + + const { tableCellsValues } = table.getMetaData('itemsInMemTable'); + + expect(tableCellsValues).toEqual([ + ['z-foo', twoDaysAgoToString], + ['a-foo', yesterdayToString], + ]); + }); + + test('filter select should have 5 options', async () => { + let testBed: TestBed; + + await act(async () => { + testBed = await setupColumnSorting({ + findItems: jest.fn().mockResolvedValue({ total: hits.length, hits }), + }); + }); + const { openSortSelect } = getActions(testBed!); + const { component, find } = testBed!; + component.update(); + + act(() => { + openSortSelect(); + }); + component.update(); + + const filterOptions = find('sortSelect').find('li'); + + expect(filterOptions.length).toBe(5); + expect(filterOptions.map((wrapper) => wrapper.text())).toEqual([ + 'Recently viewed. Checked option.Additional information ', + 'Name A-Z ', + 'Name Z-A ', + 'Recently updated ', + 'Least recently updated ', + ]); + }); + }); + describe('content editor', () => { const setupInspector = registerTestBed( WithServices(TableListViewTable), diff --git a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx index 9e69c4e0f4434..888e0a312d049 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx @@ -37,6 +37,7 @@ import type { SavedObjectsReference, } from '@kbn/content-management-content-editor'; import type { UserContentCommonSchema } from '@kbn/content-management-table-list-view-common'; +import type { RecentlyAccessed } from '@kbn/recently-accessed'; import { Table, @@ -52,6 +53,7 @@ import { type SortColumnField, getInitialSorting, saveSorting } from './componen import { useTags } from './use_tags'; import { useInRouterContext, useUrlState } from './use_url_state'; import { RowActions, TableItemsRowActions } from './types'; +import { sortByRecentlyAccessed } from './components/table_sort_select'; interface ContentEditorConfig extends Pick< @@ -116,6 +118,7 @@ export interface TableListViewTableProps< */ withoutPageTemplateWrapper?: boolean; contentEditor?: ContentEditorConfig; + recentlyAccessed?: Pick; tableCaption: string; /** Flag to force a new fetch of the table items. Whenever it changes, the `findItems()` will be called. */ @@ -145,6 +148,7 @@ export interface State { // in the query params. We might want to stop supporting both in a future release (v9.0?) stateFromURL.s = sanitizedParams.s ?? sanitizedParams.title; - if (sanitizedParams.sort === 'title' || sanitizedParams.sort === 'updatedAt') { - const field = sanitizedParams.sort === 'title' ? 'attributes.title' : 'updatedAt'; + if ( + sanitizedParams.sort === 'title' || + sanitizedParams.sort === 'updatedAt' || + sanitizedParams.sort === 'accessedAt' + ) { + const field = + sanitizedParams.sort === 'title' + ? 'attributes.title' + : sanitizedParams.sort === 'accessedAt' + ? 'accessedAt' + : 'updatedAt'; - stateFromURL.sort = { field, direction: 'asc' }; + stateFromURL.sort = { field, direction: field === 'attributes.title' ? 'asc' : 'desc' }; if (sanitizedParams.sortdir === 'desc' || sanitizedParams.sortdir === 'asc') { stateFromURL.sort.direction = sanitizedParams.sortdir; @@ -302,6 +315,7 @@ function TableListViewTableComp({ refreshListBouncer, setPageDataTestSubject, createdByEnabled = false, + recentlyAccessed, }: TableListViewTableProps) { useEffect(() => { setPageDataTestSubject(`${entityName}LandingPage`); @@ -373,6 +387,7 @@ function TableListViewTableComp({ showDeleteModal: false, hasUpdatedAtMetadata: false, hasCreatedByMetadata: false, + hasRecentlyAccessedMetadata: recentlyAccessed ? recentlyAccessed.get().length > 0 : false, selectedIds: [], searchQuery: { text: '', query: new Query(Ast.create([]), undefined, '') }, pagination: { @@ -387,7 +402,7 @@ function TableListViewTableComp({ createdBy: [], }, }; - }, [initialPageSize, entityName]); + }, [initialPageSize, entityName, recentlyAccessed]); const [state, dispatch] = useReducer(reducer, initialState); @@ -404,6 +419,7 @@ function TableListViewTableComp({ totalItems, hasUpdatedAtMetadata, hasCreatedByMetadata, + hasRecentlyAccessedMetadata, pagination, tableSort, tableFilter, @@ -433,6 +449,12 @@ function TableListViewTableComp({ } if (idx === fetchIdx.current) { + // when recentlyAccessed is available, we sort the items by the recently accessed items + // then this sort will be used as the default sort for the table + if (recentlyAccessed && recentlyAccessed.get().length > 0) { + response.hits = sortByRecentlyAccessed(response.hits, recentlyAccessed.get()); + } + dispatch({ type: 'onFetchItemsSuccess', data: { @@ -448,7 +470,7 @@ function TableListViewTableComp({ data: err, }); } - }, [searchQueryParser, searchQuery.text, findItems, onFetchSuccess]); + }, [searchQueryParser, searchQuery.text, findItems, onFetchSuccess, recentlyAccessed]); const updateQuery = useCallback( (query: Query) => { @@ -1109,6 +1131,7 @@ function TableListViewTableComp({ searchQuery={searchQuery} tableColumns={tableColumns} hasUpdatedAtMetadata={hasUpdatedAtMetadata} + hasRecentlyAccessedMetadata={hasRecentlyAccessedMetadata} tableSort={tableSort} tableFilter={tableFilter} tableItemsRowActions={tableItemsRowActions} diff --git a/packages/content-management/table_list_view_table/tsconfig.json b/packages/content-management/table_list_view_table/tsconfig.json index 09bf8256764d1..b8add47c2bfb9 100644 --- a/packages/content-management/table_list_view_table/tsconfig.json +++ b/packages/content-management/table_list_view_table/tsconfig.json @@ -34,7 +34,8 @@ "@kbn/user-profile-components", "@kbn/core-user-profile-browser", "@kbn/react-kibana-mount", - "@kbn/content-management-user-profiles" + "@kbn/content-management-user-profiles", + "@kbn/recently-accessed" ], "exclude": [ "target/**/*" diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index d2922b9a96611..380314554dedd 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -39,13 +39,13 @@ import type { SideNavComponent as ISideNavComponent, ChromeHelpMenuLink, } from '@kbn/core-chrome-browser'; +import { RecentlyAccessedService } from '@kbn/recently-accessed'; import { Logger } from '@kbn/logging'; import { DocTitleService } from './doc_title'; import { NavControlsService } from './nav_controls'; import { NavLinksService } from './nav_links'; import { ProjectNavigationService } from './project_navigation'; -import { RecentlyAccessedService } from './recently_accessed'; import { Header, LoadingIndicator, ProjectHeader } from './ui'; import { registerAnalyticsContextProvider } from './register_analytics_context_provider'; import type { InternalChromeStart } from './types'; @@ -252,7 +252,7 @@ export class ChromeService { chromeBreadcrumbs$: breadcrumbs$, logger: this.logger, }); - const recentlyAccessed = await this.recentlyAccessed.start({ http }); + const recentlyAccessed = this.recentlyAccessed.start({ http, key: 'recentlyAccessed' }); const docTitle = this.docTitle.start(); const { customBranding$ } = customBranding; const helpMenuLinks$ = navControls.getHelpMenuLinks$(); diff --git a/packages/core/chrome/core-chrome-browser-internal/tsconfig.json b/packages/core/chrome/core-chrome-browser-internal/tsconfig.json index 5d81526f2c970..d4512b515f640 100644 --- a/packages/core/chrome/core-chrome-browser-internal/tsconfig.json +++ b/packages/core/chrome/core-chrome-browser-internal/tsconfig.json @@ -16,7 +16,6 @@ "**/*.tsx", ], "kbn_references": [ - "@kbn/crypto-browser", "@kbn/i18n", "@kbn/i18n-react", "@kbn/core-injected-metadata-browser-internal", @@ -55,6 +54,7 @@ "@kbn/core-i18n-browser-mocks", "@kbn/core-theme-browser-mocks", "@kbn/react-kibana-context-render", + "@kbn/recently-accessed", ], "exclude": [ "target/**/*", diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/index.ts b/packages/core/metrics/core-metrics-collectors-server-internal/index.ts index 351129cdc8ba3..d726b0339a0bf 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/index.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/index.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -export { OsMetricsCollector } from './src/os'; -export type { OpsMetricsCollectorOptions } from './src/os'; +export { OsMetricsCollector, type OsMetricsCollectorOptions } from './src/os'; export { ProcessMetricsCollector } from './src/process'; export { ServerMetricsCollector } from './src/server'; export { EventLoopDelaysMonitor } from './src/event_loop_delays_monitor'; diff --git a/packages/core/metrics/core-metrics-collectors-server-internal/src/os.ts b/packages/core/metrics/core-metrics-collectors-server-internal/src/os.ts index 350b01a8100eb..049597cfa1f12 100644 --- a/packages/core/metrics/core-metrics-collectors-server-internal/src/os.ts +++ b/packages/core/metrics/core-metrics-collectors-server-internal/src/os.ts @@ -15,7 +15,7 @@ import { OsCgroupMetricsCollector } from './cgroup'; const getos = promisify(getosAsync); -export interface OpsMetricsCollectorOptions { +export interface OsMetricsCollectorOptions { logger: Logger; cpuPath?: string; cpuAcctPath?: string; @@ -24,7 +24,7 @@ export interface OpsMetricsCollectorOptions { export class OsMetricsCollector implements MetricsCollector { private readonly cgroupCollector: OsCgroupMetricsCollector; - constructor(options: OpsMetricsCollectorOptions) { + constructor(options: OsMetricsCollectorOptions) { this.cgroupCollector = new OsCgroupMetricsCollector({ ...options, logger: options.logger.get('cgroup'), diff --git a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts index 2e358fc121bce..f35213a0e0480 100644 --- a/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts +++ b/packages/core/metrics/core-metrics-server-internal/src/ops_metrics_collector.ts @@ -7,16 +7,22 @@ */ import { Server as HapiServer } from '@hapi/hapi'; +import type { Logger } from '@kbn/logging'; import type { OpsMetrics, MetricsCollector } from '@kbn/core-metrics-server'; import type { AgentStatsProvider } from '@kbn/core-elasticsearch-client-server-internal'; import { ProcessMetricsCollector, OsMetricsCollector, - type OpsMetricsCollectorOptions, ServerMetricsCollector, ElasticsearchClientsMetricsCollector, } from '@kbn/core-metrics-collectors-server-internal'; +export interface OpsMetricsCollectorOptions { + logger: Logger; + cpuPath?: string; + cpuAcctPath?: string; +} + export class OpsMetricsCollector implements MetricsCollector { private readonly processCollector: ProcessMetricsCollector; private readonly osCollector: OsMetricsCollector; diff --git a/packages/core/security/core-security-server-internal/src/security_route_handler_context.ts b/packages/core/security/core-security-server-internal/src/security_route_handler_context.ts index 4fa328782dd0e..bae1c11d152a4 100644 --- a/packages/core/security/core-security-server-internal/src/security_route_handler_context.ts +++ b/packages/core/security/core-security-server-internal/src/security_route_handler_context.ts @@ -26,6 +26,16 @@ export class CoreSecurityRouteHandlerContext implements SecurityRequestHandlerCo if (this.#authc == null) { this.#authc = { getCurrentUser: () => this.securityStart.authc.getCurrentUser(this.request), + apiKeys: { + areAPIKeysEnabled: () => this.securityStart.authc.apiKeys.areAPIKeysEnabled(), + create: (createParams) => + this.securityStart.authc.apiKeys.create(this.request, createParams), + update: (updateParams) => + this.securityStart.authc.apiKeys.update(this.request, updateParams), + validate: (apiKeyParams) => this.securityStart.authc.apiKeys.validate(apiKeyParams), + invalidate: (apiKeyParams) => + this.securityStart.authc.apiKeys.invalidate(this.request, apiKeyParams), + }, }; } return this.#authc; diff --git a/packages/core/security/core-security-server-internal/src/utils/convert_security_api.test.ts b/packages/core/security/core-security-server-internal/src/utils/convert_security_api.test.ts index 7c2e49092f73e..40d9e788ea01b 100644 --- a/packages/core/security/core-security-server-internal/src/utils/convert_security_api.test.ts +++ b/packages/core/security/core-security-server-internal/src/utils/convert_security_api.test.ts @@ -15,6 +15,16 @@ describe('convertSecurityApi', () => { const source: CoreSecurityDelegateContract = { authc: { getCurrentUser: jest.fn(), + apiKeys: { + areAPIKeysEnabled: jest.fn(), + areCrossClusterAPIKeysEnabled: jest.fn(), + validate: jest.fn(), + invalidate: jest.fn(), + invalidateAsInternalUser: jest.fn(), + grantAsInternalUser: jest.fn(), + create: jest.fn(), + update: jest.fn(), + }, }, audit: { asScoped: jest.fn().mockReturnValue(createAuditLoggerMock.create()), @@ -23,6 +33,7 @@ describe('convertSecurityApi', () => { }; const output = convertSecurityApi(source); expect(output.authc.getCurrentUser).toBe(source.authc.getCurrentUser); + expect(output.authc.apiKeys).toBe(source.authc.apiKeys); expect(output.audit.asScoped).toBe(source.audit.asScoped); expect(output.audit.withoutRequest).toBe(source.audit.withoutRequest); }); diff --git a/packages/core/security/core-security-server-internal/src/utils/default_implementation.test.ts b/packages/core/security/core-security-server-internal/src/utils/default_implementation.test.ts index e4348404671b9..bc7fac96b7dd3 100644 --- a/packages/core/security/core-security-server-internal/src/utils/default_implementation.test.ts +++ b/packages/core/security/core-security-server-internal/src/utils/default_implementation.test.ts @@ -23,6 +23,15 @@ describe('getDefaultSecurityImplementation', () => { }); }); + describe('authc.apiKeys', () => { + it('returns stub object', async () => { + const { apiKeys } = implementation.authc; + const areAPIKeysEnabled = await apiKeys.areAPIKeysEnabled(); + + expect(areAPIKeysEnabled).toBe(false); + }); + }); + describe('audit.asScoped', () => { it('returns null', async () => { const logger = implementation.audit.asScoped({} as any); diff --git a/packages/core/security/core-security-server-internal/src/utils/default_implementation.ts b/packages/core/security/core-security-server-internal/src/utils/default_implementation.ts index 91819807f1064..8eaeb7b2577b5 100644 --- a/packages/core/security/core-security-server-internal/src/utils/default_implementation.ts +++ b/packages/core/security/core-security-server-internal/src/utils/default_implementation.ts @@ -8,10 +8,23 @@ import type { CoreSecurityDelegateContract } from '@kbn/core-security-server'; +const API_KEYS_DISABLED_ERROR = new Error('API keys are disabled'); +const REJECT_WHEN_API_KEYS_DISABLED = () => Promise.reject(API_KEYS_DISABLED_ERROR); + export const getDefaultSecurityImplementation = (): CoreSecurityDelegateContract => { return { authc: { getCurrentUser: () => null, + apiKeys: { + areAPIKeysEnabled: () => Promise.resolve(false), + areCrossClusterAPIKeysEnabled: () => Promise.resolve(false), + create: REJECT_WHEN_API_KEYS_DISABLED, + update: REJECT_WHEN_API_KEYS_DISABLED, + grantAsInternalUser: REJECT_WHEN_API_KEYS_DISABLED, + validate: REJECT_WHEN_API_KEYS_DISABLED, + invalidate: REJECT_WHEN_API_KEYS_DISABLED, + invalidateAsInternalUser: REJECT_WHEN_API_KEYS_DISABLED, + }, }, audit: { asScoped: () => { diff --git a/packages/core/security/core-security-server-mocks/index.ts b/packages/core/security/core-security-server-mocks/index.ts index 23c49282252f0..c834759973c1e 100644 --- a/packages/core/security/core-security-server-mocks/index.ts +++ b/packages/core/security/core-security-server-mocks/index.ts @@ -9,3 +9,4 @@ export { securityServiceMock } from './src/security_service.mock'; export type { InternalSecurityStartMock, SecurityStartMock } from './src/security_service.mock'; export { auditLoggerMock } from './src/audit.mock'; +export { apiKeysMock } from './src/api_keys.mock'; diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.mock.ts b/packages/core/security/core-security-server-mocks/src/api_keys.mock.ts similarity index 59% rename from x-pack/plugins/security/server/authentication/api_keys/api_keys.mock.ts rename to packages/core/security/core-security-server-mocks/src/api_keys.mock.ts index cfa857ca833a2..108f8380264e6 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.mock.ts +++ b/packages/core/security/core-security-server-mocks/src/api_keys.mock.ts @@ -1,16 +1,15 @@ /* * 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. + * 2.0 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 or the Server + * Side Public License, v 1. */ -import type { PublicMethodsOf } from '@kbn/utility-types'; - -import type { APIKeys } from './api_keys'; +import type { APIKeysService } from '@kbn/core-security-server'; export const apiKeysMock = { - create: (): jest.Mocked> => ({ + create: (): jest.MockedObjectDeep => ({ areAPIKeysEnabled: jest.fn(), areCrossClusterAPIKeysEnabled: jest.fn(), create: jest.fn(), diff --git a/packages/core/security/core-security-server-mocks/src/security_service.mock.ts b/packages/core/security/core-security-server-mocks/src/security_service.mock.ts index d833048990ff5..86a39af3b16d5 100644 --- a/packages/core/security/core-security-server-mocks/src/security_service.mock.ts +++ b/packages/core/security/core-security-server-mocks/src/security_service.mock.ts @@ -15,6 +15,7 @@ import type { InternalSecurityServiceSetup, InternalSecurityServiceStart, } from '@kbn/core-security-server-internal'; +import { apiKeysMock } from './api_keys.mock'; import { auditServiceMock, type MockedAuditService } from './audit.mock'; import { mockAuthenticatedUser, MockAuthenticatedUserProps } from '@kbn/core-security-common/mocks'; @@ -35,6 +36,7 @@ const createStartMock = (): SecurityStartMock => { const mock = { authc: { getCurrentUser: jest.fn(), + apiKeys: apiKeysMock.create(), }, audit: auditServiceMock.create(), }; @@ -61,6 +63,7 @@ const createInternalStartMock = (): InternalSecurityStartMock => { const mock = { authc: { getCurrentUser: jest.fn(), + apiKeys: apiKeysMock.create(), }, audit: auditServiceMock.create(), }; @@ -82,6 +85,13 @@ const createRequestHandlerContextMock = () => { const mock: jest.MockedObjectDeep = { authc: { getCurrentUser: jest.fn(), + apiKeys: { + areAPIKeysEnabled: jest.fn(), + create: jest.fn(), + update: jest.fn(), + validate: jest.fn(), + invalidate: jest.fn(), + }, }, audit: { logger: { diff --git a/packages/core/security/core-security-server/index.ts b/packages/core/security/core-security-server/index.ts index 6a111ab6e27ab..b5dd091c7b87a 100644 --- a/packages/core/security/core-security-server/index.ts +++ b/packages/core/security/core-security-server/index.ts @@ -26,4 +26,26 @@ export type { AuditRequest, } from './src/audit_logging/audit_events'; export type { AuditLogger } from './src/audit_logging/audit_logger'; + +export type { + APIKeysServiceWithContext, + APIKeysService, + CreateAPIKeyParams, + CreateAPIKeyResult, + InvalidateAPIKeyResult, + InvalidateAPIKeysParams, + ValidateAPIKeyParams, + CreateRestAPIKeyParams, + CreateRestAPIKeyWithKibanaPrivilegesParams, + CreateCrossClusterAPIKeyParams, + GrantAPIKeyResult, + UpdateAPIKeyParams, + UpdateAPIKeyResult, + UpdateCrossClusterAPIKeyParams, + UpdateRestAPIKeyParams, + UpdateRestAPIKeyWithKibanaPrivilegesParams, +} from './src/authentication/api_keys'; + +export type { KibanaPrivilegesType, ElasticsearchPrivilegesType } from './src/roles'; +export { isCreateRestAPIKeyParams } from './src/authentication/api_keys'; export type { CoreFipsService } from './src/fips'; diff --git a/packages/core/security/core-security-server/src/authc.ts b/packages/core/security/core-security-server/src/authc.ts index 97654104858ea..85ba4fc71542a 100644 --- a/packages/core/security/core-security-server/src/authc.ts +++ b/packages/core/security/core-security-server/src/authc.ts @@ -8,6 +8,7 @@ import type { KibanaRequest } from '@kbn/core-http-server'; import type { AuthenticatedUser } from '@kbn/core-security-common'; +import type { APIKeysService } from './authentication/api_keys'; /** * Core's authentication service @@ -22,4 +23,5 @@ export interface CoreAuthenticationService { * @param request The request to retrieve the authenticated user for. */ getCurrentUser(request: KibanaRequest): AuthenticatedUser | null; + apiKeys: APIKeysService; } diff --git a/packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts b/packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts new file mode 100644 index 0000000000000..e842c38d8674d --- /dev/null +++ b/packages/core/security/core-security-server/src/authentication/api_keys/api_keys.ts @@ -0,0 +1,268 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import type { estypes } from '@elastic/elasticsearch'; + +import type { KibanaRequest } from '@kbn/core-http-server'; + +import { ElasticsearchPrivilegesType, KibanaPrivilegesType } from '../../roles'; + +/** + * Interface for managing API keys in Elasticsearch, including creation, + * validation, and invalidation of API keys, + * as well as checking the status of API key features. + */ +export interface APIKeys { + /** + * Determines if API Keys are enabled in Elasticsearch. + */ + areAPIKeysEnabled(): Promise; + + /** + * Determines if Cross-Cluster API Keys are enabled in Elasticsearch. + */ + areCrossClusterAPIKeysEnabled(): Promise; + + /** + * Tries to create an API key for the current user. + * + * Returns newly created API key or `null` if API keys are disabled. + * + * User needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys. + * + * @param request Request instance. + * @param createParams The params to create an API key + */ + create( + request: KibanaRequest, + createParams: CreateAPIKeyParams + ): Promise; + + /** + * Attempts update an API key with the provided 'role_descriptors' and 'metadata' + * + * Returns `updated`, `true` if the update was successful, `false` if there was nothing to update + * + * User needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys. + * + * @param request Request instance. + * @param updateParams The params to edit an API key + */ + update( + request: KibanaRequest, + updateParams: UpdateAPIKeyParams + ): Promise; + + /** + * Tries to grant an API key for the current user. + * @param request Request instance. + * @param createParams Create operation parameters. + */ + grantAsInternalUser( + request: KibanaRequest, + createParams: CreateRestAPIKeyParams | CreateRestAPIKeyWithKibanaPrivilegesParams + ): Promise; + + /** + * Tries to validate an API key. + * @param apiKeyPrams ValidateAPIKeyParams. + */ + validate(apiKeyPrams: ValidateAPIKeyParams): Promise; + + /** + * Tries to invalidate an API keys. + * @param request Request instance. + * @param params The params to invalidate an API keys. + */ + invalidate( + request: KibanaRequest, + params: InvalidateAPIKeysParams + ): Promise; + + /** + * Tries to invalidate the API keys by using the internal user. + * @param params The params to invalidate the API keys. + */ + invalidateAsInternalUser(params: InvalidateAPIKeysParams): Promise; +} + +export type CreateAPIKeyParams = + | CreateRestAPIKeyParams + | CreateRestAPIKeyWithKibanaPrivilegesParams + | CreateCrossClusterAPIKeyParams; + +/** + * Response of Kibana Create API key endpoint. + */ +export type CreateAPIKeyResult = estypes.SecurityCreateApiKeyResponse; + +export interface CreateRestAPIKeyParams { + type?: 'rest'; + expiration?: string; + name: string; + role_descriptors: Record; + metadata?: { [key: string]: any }; +} + +export interface CreateRestAPIKeyWithKibanaPrivilegesParams { + type?: 'rest'; + expiration?: string; + name: string; + metadata?: { [key: string]: any }; + kibana_role_descriptors: Record< + string, + { + elasticsearch: ElasticsearchPrivilegesType & { [key: string]: unknown }; + kibana: KibanaPrivilegesType; + } + >; +} + +export interface CreateCrossClusterAPIKeyParams { + type: 'cross_cluster'; + expiration?: string; + name: string; + metadata?: { [key: string]: any }; + access: { + search?: Array<{ + names: string[]; + query?: unknown; + field_security?: unknown; + allow_restricted_indices?: boolean; + }>; + replication?: Array<{ + names: string[]; + }>; + }; +} + +export interface GrantAPIKeyResult { + /** + * Unique id for this API key + */ + id: string; + /** + * Name for this API key + */ + name: string; + /** + * Generated API key + */ + api_key: string; +} + +/** + * Represents the parameters for validating API Key credentials. + */ +export interface ValidateAPIKeyParams { + /** + * Unique id for this API key + */ + id: string; + + /** + * Generated API Key (secret) + */ + api_key: string; +} + +/** + * Represents the params for invalidating multiple API keys + */ +export interface InvalidateAPIKeysParams { + /** + * List of unique API key IDs + */ + ids: string[]; +} + +/** + * The return value when invalidating an API key in Elasticsearch. + */ +export interface InvalidateAPIKeyResult { + /** + * The IDs of the API keys that were invalidated as part of the request. + */ + invalidated_api_keys: string[]; + /** + * The IDs of the API keys that were already invalidated. + */ + previously_invalidated_api_keys: string[]; + /** + * The number of errors that were encountered when invalidating the API keys. + */ + error_count: number; + /** + * Details about these errors. This field is not present in the response when error_count is 0. + */ + error_details?: Array<{ + type?: string; + reason?: string; + caused_by?: { + type?: string; + reason?: string; + }; + }>; +} + +/** + * Response of Kibana Update API key endpoint. + */ +export type UpdateAPIKeyResult = estypes.SecurityUpdateApiKeyResponse; + +/** + * Request body of Kibana Update API key endpoint. + */ +export type UpdateAPIKeyParams = + | UpdateRestAPIKeyParams + | UpdateCrossClusterAPIKeyParams + | UpdateRestAPIKeyWithKibanaPrivilegesParams; + +export interface UpdateRestAPIKeyParams { + id: string; + type?: 'rest'; + expiration?: string; + role_descriptors: Record; + metadata?: { [key: string]: any }; +} + +export interface UpdateCrossClusterAPIKeyParams { + id: string; + type: 'cross_cluster'; + expiration?: string; + metadata?: { [key: string]: any }; + access: { + search?: Array<{ + names: string[]; + query?: unknown; + field_security?: unknown; + allow_restricted_indices?: boolean; + }>; + replication?: Array<{ + names: string[]; + }>; + }; +} + +export interface UpdateRestAPIKeyWithKibanaPrivilegesParams { + id: string; + type?: 'rest'; + expiration?: string; + metadata?: { [key: string]: any }; + kibana_role_descriptors: Record< + string, + { + elasticsearch: ElasticsearchPrivilegesType & { [key: string]: unknown }; + kibana: KibanaPrivilegesType; + } + >; +} + +export function isCreateRestAPIKeyParams(params: any): params is CreateRestAPIKeyParams { + return 'role_descriptors' in params; +} diff --git a/packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts b/packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts new file mode 100644 index 0000000000000..7090f7312774f --- /dev/null +++ b/packages/core/security/core-security-server/src/authentication/api_keys/api_keys_context.ts @@ -0,0 +1,64 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import type { + CreateAPIKeyParams, + CreateAPIKeyResult, + UpdateAPIKeyParams, + UpdateAPIKeyResult, + ValidateAPIKeyParams, + InvalidateAPIKeyResult, + InvalidateAPIKeysParams, +} from './api_keys'; + +/** + * Public API Keys service exposed through core context to manage + * API keys in Elasticsearch, including creation, + * validation, and invalidation of API keys, + * as well as checking the status of API key features. + */ +export interface APIKeysServiceWithContext { + /** + * Determines if API Keys are enabled in Elasticsearch. + */ + areAPIKeysEnabled(): Promise; + + /** + * Tries to create an API key for the current user. + * + * Returns newly created API key or `null` if API keys are disabled. + * + * User needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys. + * + * @param createParams The params to create an API key + */ + create(createParams: CreateAPIKeyParams): Promise; + + /** + * Attempts update an API key with the provided 'role_descriptors' and 'metadata' + * + * Returns `updated`, `true` if the update was successful, `false` if there was nothing to update + * + * User needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys. + * + * @param updateParams The params to edit an API key + */ + update(updateParams: UpdateAPIKeyParams): Promise; + + /** + * Tries to validate an API key. + * @param apiKeyPrams ValidateAPIKeyParams. + */ + validate(apiKeyPrams: ValidateAPIKeyParams): Promise; + + /** + * Tries to invalidate an API keys. + * @param params The params to invalidate an API keys. + */ + invalidate(params: InvalidateAPIKeysParams): Promise; +} diff --git a/packages/core/security/core-security-server/src/authentication/api_keys/index.ts b/packages/core/security/core-security-server/src/authentication/api_keys/index.ts new file mode 100644 index 0000000000000..da7163bb50879 --- /dev/null +++ b/packages/core/security/core-security-server/src/authentication/api_keys/index.ts @@ -0,0 +1,27 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export type { + APIKeys as APIKeysService, + CreateAPIKeyParams, + CreateAPIKeyResult, + InvalidateAPIKeyResult, + InvalidateAPIKeysParams, + ValidateAPIKeyParams, + CreateRestAPIKeyParams, + CreateRestAPIKeyWithKibanaPrivilegesParams, + CreateCrossClusterAPIKeyParams, + GrantAPIKeyResult, + UpdateAPIKeyParams, + UpdateAPIKeyResult, + UpdateCrossClusterAPIKeyParams, + UpdateRestAPIKeyParams, + UpdateRestAPIKeyWithKibanaPrivilegesParams, +} from './api_keys'; +export type { APIKeysServiceWithContext } from './api_keys_context'; +export { isCreateRestAPIKeyParams } from './api_keys'; diff --git a/packages/core/security/core-security-server/src/request_handler_context.ts b/packages/core/security/core-security-server/src/request_handler_context.ts index 37915c24ddaa1..6cb13b3afb9a8 100644 --- a/packages/core/security/core-security-server/src/request_handler_context.ts +++ b/packages/core/security/core-security-server/src/request_handler_context.ts @@ -7,7 +7,9 @@ */ import type { AuthenticatedUser } from '@kbn/core-security-common'; + import { AuditLogger } from './audit_logging/audit_logger'; +import type { APIKeysServiceWithContext } from './authentication/api_keys'; export interface SecurityRequestHandlerContext { authc: AuthcRequestHandlerContext; @@ -16,6 +18,7 @@ export interface SecurityRequestHandlerContext { export interface AuthcRequestHandlerContext { getCurrentUser(): AuthenticatedUser | null; + apiKeys: APIKeysServiceWithContext; } export interface AuditRequestHandlerContext { diff --git a/packages/core/security/core-security-server/src/roles/index.ts b/packages/core/security/core-security-server/src/roles/index.ts new file mode 100644 index 0000000000000..420f6780fdd85 --- /dev/null +++ b/packages/core/security/core-security-server/src/roles/index.ts @@ -0,0 +1,9 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export type { ElasticsearchPrivilegesType, KibanaPrivilegesType } from './schema'; diff --git a/packages/core/security/core-security-server/src/roles/schema.ts b/packages/core/security/core-security-server/src/roles/schema.ts new file mode 100644 index 0000000000000..693916ef3d9b3 --- /dev/null +++ b/packages/core/security/core-security-server/src/roles/schema.ts @@ -0,0 +1,42 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +/** + * Type representing Elasticsearch specific portion of the role definition. + */ +export interface ElasticsearchPrivilegesType { + cluster?: string[]; + remote_cluster?: Array<{ + privileges: string[]; + clusters: string[]; + }>; + indices?: Array<{ + names: string[]; + field_security?: Record<'grant' | 'except', string[]>; + privileges: string[]; + query?: string; + allow_restricted_indices?: boolean; + }>; + remote_indices?: Array<{ + clusters: string[]; + names: string[]; + field_security?: Record<'grant' | 'except', string[]>; + privileges: string[]; + query?: string; + allow_restricted_indices?: boolean; + }>; + run_as?: string[]; +} +/** + * Type representing Kibana specific portion of the role definition. + */ +export type KibanaPrivilegesType = Array<{ + spaces: string[]; + base?: string[]; + feature?: Record; +}>; diff --git a/packages/deeplinks/observability/locators/dataset_quality.ts b/packages/deeplinks/observability/locators/dataset_quality.ts index bfa760bf62c06..e30648e3f129c 100644 --- a/packages/deeplinks/observability/locators/dataset_quality.ts +++ b/packages/deeplinks/observability/locators/dataset_quality.ts @@ -8,7 +8,7 @@ import { SerializableRecord } from '@kbn/utility-types'; -export const DATASET_QUALITY_LOCATOR_ID = 'DATASET_QUALITY_LOCATOR'; +export const DATA_QUALITY_LOCATOR_ID = 'DATA_QUALITY_LOCATOR'; // eslint-disable-next-line @typescript-eslint/consistent-type-definitions type RefreshInterval = { @@ -23,11 +23,22 @@ type TimeRangeConfig = { refresh: RefreshInterval; }; +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +type DatasetConfig = { + rawName: string; + type: string; + name: string; + namespace: string; +}; + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions type Filters = { timeRange: TimeRangeConfig; }; -export interface DatasetQualityLocatorParams extends SerializableRecord { +export interface DataQualityLocatorParams extends SerializableRecord { filters?: Filters; + flyout?: { + dataset: DatasetConfig; + }; } diff --git a/packages/deeplinks/search/constants.ts b/packages/deeplinks/search/constants.ts index 3fdcc78bb68a1..8aa53658320f5 100644 --- a/packages/deeplinks/search/constants.ts +++ b/packages/deeplinks/search/constants.ts @@ -8,7 +8,7 @@ export const ENTERPRISE_SEARCH_APP_ID = 'enterpriseSearch'; export const ENTERPRISE_SEARCH_CONTENT_APP_ID = 'enterpriseSearchContent'; -export const ENTERPRISE_SEARCH_INFERENCE_ENDPOINTS_APP_ID = 'enterpriseSearchInferenceEndpoints'; +export const ENTERPRISE_SEARCH_RELEVANCE_APP_ID = 'enterpriseSearchRelevance'; export const ENTERPRISE_SEARCH_APPLICATIONS_APP_ID = 'enterpriseSearchApplications'; export const ENTERPRISE_SEARCH_ANALYTICS_APP_ID = 'enterpriseSearchAnalytics'; export const ENTERPRISE_SEARCH_APPSEARCH_APP_ID = 'appSearch'; diff --git a/packages/deeplinks/search/deep_links.ts b/packages/deeplinks/search/deep_links.ts index f004d1b2c9dd6..4b32ec9757bde 100644 --- a/packages/deeplinks/search/deep_links.ts +++ b/packages/deeplinks/search/deep_links.ts @@ -12,6 +12,7 @@ import { ENTERPRISE_SEARCH_APP_ID, ENTERPRISE_SEARCH_CONTENT_APP_ID, ENTERPRISE_SEARCH_APPLICATIONS_APP_ID, + ENTERPRISE_SEARCH_RELEVANCE_APP_ID, ENTERPRISE_SEARCH_ANALYTICS_APP_ID, ENTERPRISE_SEARCH_APPSEARCH_APP_ID, ENTERPRISE_SEARCH_WORKPLACESEARCH_APP_ID, @@ -23,6 +24,7 @@ import { export type EnterpriseSearchApp = typeof ENTERPRISE_SEARCH_APP_ID; export type EnterpriseSearchContentApp = typeof ENTERPRISE_SEARCH_CONTENT_APP_ID; export type EnterpriseSearchApplicationsApp = typeof ENTERPRISE_SEARCH_APPLICATIONS_APP_ID; +export type EnterpriseSearchRelevanceApp = typeof ENTERPRISE_SEARCH_RELEVANCE_APP_ID; export type EnterpriseSearchAnalyticsApp = typeof ENTERPRISE_SEARCH_ANALYTICS_APP_ID; export type EnterpriseSearchAppsearchApp = typeof ENTERPRISE_SEARCH_APPSEARCH_APP_ID; export type EnterpriseSearchWorkplaceSearchApp = typeof ENTERPRISE_SEARCH_WORKPLACESEARCH_APP_ID; @@ -38,10 +40,13 @@ export type ApplicationsLinkId = 'searchApplications' | 'playground'; export type AppsearchLinkId = 'engines'; +export type RelevanceLinkId = 'inferenceEndpoints'; + export type DeepLinkId = | EnterpriseSearchApp | EnterpriseSearchContentApp | EnterpriseSearchApplicationsApp + | EnterpriseSearchRelevanceApp | EnterpriseSearchAnalyticsApp | EnterpriseSearchAppsearchApp | EnterpriseSearchWorkplaceSearchApp @@ -52,4 +57,5 @@ export type DeepLinkId = | SearchHomepage | `${EnterpriseSearchContentApp}:${ContentLinkId}` | `${EnterpriseSearchApplicationsApp}:${ApplicationsLinkId}` - | `${EnterpriseSearchAppsearchApp}:${AppsearchLinkId}`; + | `${EnterpriseSearchAppsearchApp}:${AppsearchLinkId}` + | `${EnterpriseSearchRelevanceApp}:${RelevanceLinkId}`; diff --git a/packages/deeplinks/search/index.ts b/packages/deeplinks/search/index.ts index 663d625e9fd72..a18f0cb31426f 100644 --- a/packages/deeplinks/search/index.ts +++ b/packages/deeplinks/search/index.ts @@ -9,7 +9,7 @@ export { ENTERPRISE_SEARCH_APP_ID, ENTERPRISE_SEARCH_CONTENT_APP_ID, - ENTERPRISE_SEARCH_INFERENCE_ENDPOINTS_APP_ID, + ENTERPRISE_SEARCH_RELEVANCE_APP_ID, ENTERPRISE_SEARCH_APPLICATIONS_APP_ID, ENTERPRISE_SEARCH_ANALYTICS_APP_ID, ENTERPRISE_SEARCH_APPSEARCH_APP_ID, diff --git a/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.test.ts b/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.test.ts index 28a0c39c998df..7cb64b51427e0 100644 --- a/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.test.ts +++ b/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.test.ts @@ -114,13 +114,9 @@ describe('updateRule', () => { }, ], }); - expect(http.put.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alerting/rule/12%2F3", - Object { - "body": "{\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[],\\"alert_delay\\":{\\"active\\":10}}", - }, - ] - `); + + expect(http.put).toHaveBeenCalledWith('/api/alerting/rule/12%2F3', { + body: '{"name":"test","tags":["foo"],"schedule":{"interval":"1m"},"params":{},"actions":[{"group":"default","id":"2","params":{},"frequency":{"notify_when":"onActionGroupChange","throttle":null,"summary":false},"use_alert_data_for_template":false},{"id":".test-system-action","params":{}}],"alert_delay":{"active":10}}', + }); }); }); diff --git a/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.ts b/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.ts index 7d9dbf71211ee..841778eaa52ee 100644 --- a/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.ts +++ b/packages/kbn-alerts-ui-shared/src/common/apis/update_rule/update_rule.ts @@ -44,7 +44,7 @@ export async function updateRule({ const res = await http.put>( `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}`, { - body: JSON.stringify(transformUpdateRuleBody(pick(rule, UPDATE_FIELDS))), + body: JSON.stringify(transformUpdateRuleBody(pick(rule, UPDATE_FIELDS_WITH_ACTIONS))), } ); return transformRule(res); diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 9cecd6df6d6f6..59cd87288e8ec 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -301,7 +301,8 @@ "metrics", "name", "staticFields", - "type" + "type", + "version" ], "entity-discovery-api-key": [ "apiKey" diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index f0df154b4f012..47602ca181b5e 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1028,6 +1028,9 @@ }, "type": { "type": "keyword" + }, + "version": { + "type": "keyword" } } }, diff --git a/packages/kbn-config-schema/README.md b/packages/kbn-config-schema/README.md index 10d96578d4689..2074ab7698e63 100644 --- a/packages/kbn-config-schema/README.md +++ b/packages/kbn-config-schema/README.md @@ -138,6 +138,7 @@ __Options:__ * `validate: (value: number) => string | void` - defines a custom validator function, see [Custom validation](#custom-validation) section for more details. * `min: number` - defines a minimum value the number should have. * `max: number` - defines a maximum value the number should have. + * `unsafe: boolean` - if true, will accept unsafe numbers (integers > 2^53). __Usage:__ ```typescript @@ -460,8 +461,12 @@ const valueSchema = schema.duration({ defaultValue: '70ms' }); ``` __Notes:__ -* The string value for `schema.duration()` supports the following optional suffixes: `ms`, `s`, `m`, `h`, `d`, `w`, `M` and `Y`. The default suffix is `ms`. +* The string value for `schema.duration()` supports the following optional suffixes: `ms`, `s`, `m`, `h`, `d`, `w`, `M` and `y`. The default suffix is `ms`. * The number value is treated as a number of milliseconds and hence should be a positive integer, e.g. `100` is equal to `'100ms'`. +* Multi-unit duration strings are supported (`1m30s`). + * Spaces are not allowed. + * It allows any order in the units (`1m30s1d`). + * It allows the same unit to be specified multiple times (`1m30s50m` is the same as `51m30s`). #### `schema.conditional()` diff --git a/packages/kbn-config-schema/src/duration/index.ts b/packages/kbn-config-schema/src/duration/index.ts index 1b1d47a24abd3..5111a603bfbdc 100644 --- a/packages/kbn-config-schema/src/duration/index.ts +++ b/packages/kbn-config-schema/src/duration/index.ts @@ -6,29 +6,36 @@ * Side Public License, v 1. */ -import { Duration, duration as momentDuration, DurationInputArg2, isDuration } from 'moment'; +import { Duration, duration as momentDuration, isDuration } from 'moment'; export type { Duration }; export { isDuration }; -const timeFormatRegex = /^(0|[1-9][0-9]*)(ms|s|m|h|d|w|M|Y)$/; +const timeFormatRegex = /^(0|[1-9][0-9]*)(ms|s|m|h|d|w|M|y|Y)(.*)$/; +type TimeUnitString = 'ms' | 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y' | 'Y'; // Moment officially supports lowercased 'y', but keeping 'Y' for BWC -function stringToDuration(text: string) { +function stringToDuration(text: string): Duration { const result = timeFormatRegex.exec(text); if (!result) { const number = Number(text); if (typeof number !== 'number' || isNaN(number)) { throw new Error( `Failed to parse value as time value. Value must be a duration in milliseconds, or follow the format ` + - `[ms|s|m|h|d|w|M|Y] (e.g. '70ms', '5s', '3d', '1Y'), where the duration is a safe positive integer.` + `[ms|s|m|h|d|w|M|y] (e.g. '70ms', '5s', '3d', '1y', '1m30s'), where the duration is a safe positive integer.` ); } return numberToDuration(number); } const count = parseInt(result[1], 10); - const unit = result[2] as DurationInputArg2; + const unit = result[2] as TimeUnitString; + const rest = result[3]; - return momentDuration(count, unit); + const duration = momentDuration(count, unit as Exclude); // Moment still supports capital 'Y', but officially (and type-wise), it doesn't. + + if (rest) { + return duration.add(stringToDuration(rest)); + } + return duration; } function numberToDuration(numberMs: number) { diff --git a/packages/kbn-config-schema/src/types/duration_type.test.ts b/packages/kbn-config-schema/src/types/duration_type.test.ts index 18327b65cd1f6..779962b6354b8 100644 --- a/packages/kbn-config-schema/src/types/duration_type.test.ts +++ b/packages/kbn-config-schema/src/types/duration_type.test.ts @@ -24,6 +24,22 @@ test('handles number', () => { expect(duration().validate(123000)).toEqual(momentDuration(123000)); }); +test('handles multi-unit', () => { + expect(duration().validate('1m30s')).toEqual(momentDuration(90000)); + expect(duration().validate('1m30s70ms')).toEqual(momentDuration(90070)); +}); + +test.each([60000, '60000', '60000ms', '60s', '1m', '1m0s'])( + 'multiple ways of introducing 1 minute: %p', + (d) => { + expect(duration().validate(d)).toEqual(momentDuration(60000)); + } +); + +test('it supports years as Y and y', () => { + expect(duration().validate('1y')).toEqual(duration().validate('1Y')); +}); + test('is required by default', () => { expect(() => duration().validate(undefined)).toThrowErrorMatchingInlineSnapshot( `"expected value of type [moment.Duration] but got [undefined]"` @@ -184,10 +200,10 @@ test('returns error when not valid string or non-safe positive integer', () => { ); expect(() => duration().validate('123foo')).toThrowErrorMatchingInlineSnapshot( - `"Failed to parse value as time value. Value must be a duration in milliseconds, or follow the format [ms|s|m|h|d|w|M|Y] (e.g. '70ms', '5s', '3d', '1Y'), where the duration is a safe positive integer."` + `"Failed to parse value as time value. Value must be a duration in milliseconds, or follow the format [ms|s|m|h|d|w|M|y] (e.g. '70ms', '5s', '3d', '1y', '1m30s'), where the duration is a safe positive integer."` ); expect(() => duration().validate('123 456')).toThrowErrorMatchingInlineSnapshot( - `"Failed to parse value as time value. Value must be a duration in milliseconds, or follow the format [ms|s|m|h|d|w|M|Y] (e.g. '70ms', '5s', '3d', '1Y'), where the duration is a safe positive integer."` + `"Failed to parse value as time value. Value must be a duration in milliseconds, or follow the format [ms|s|m|h|d|w|M|y] (e.g. '70ms', '5s', '3d', '1y', '1m30s'), where the duration is a safe positive integer."` ); }); diff --git a/packages/kbn-config-schema/src/types/number_type.test.ts b/packages/kbn-config-schema/src/types/number_type.test.ts index 3077d41d29cc5..0e1fcdd1dc028 100644 --- a/packages/kbn-config-schema/src/types/number_type.test.ts +++ b/packages/kbn-config-schema/src/types/number_type.test.ts @@ -64,6 +64,26 @@ describe('#max', () => { }); }); +describe('#unsafe', () => { + it('rejects unsafe numbers when undefined', () => { + expect(() => schema.number().validate(9007199254740992)).toThrowErrorMatchingInlineSnapshot( + `"\\"value\\" must be a safe number"` + ); + }); + + it('rejects unsafe numbers when false', () => { + expect(() => + schema.number({ unsafe: false }).validate(9007199254740992) + ).toThrowErrorMatchingInlineSnapshot(`"\\"value\\" must be a safe number"`); + }); + + it('accepts unsafe numbers when true', () => { + expect(schema.number({ unsafe: true }).validate(9007199254740992)).toBeGreaterThan( + 9007199254740991 + ); + }); +}); + describe('#defaultValue', () => { test('returns default when number is undefined', () => { expect(schema.number({ defaultValue: 2 }).validate(undefined)).toBe(2); diff --git a/packages/kbn-config-schema/src/types/number_type.ts b/packages/kbn-config-schema/src/types/number_type.ts index 6b86fe7e7fc71..195e3916feb96 100644 --- a/packages/kbn-config-schema/src/types/number_type.ts +++ b/packages/kbn-config-schema/src/types/number_type.ts @@ -13,6 +13,12 @@ import { Type, TypeOptions } from './type'; export type NumberOptions = TypeOptions & { min?: number; max?: number; + /** + * When set to true, will accept unsafe numbers (integers > 2^53). + * Otherwise, unsafe numbers will fail validation. + * Default: `false` + */ + unsafe?: boolean; }; export class NumberType extends Type { @@ -21,10 +27,12 @@ export class NumberType extends Type { if (options.min !== undefined) { schema = schema.min(options.min); } - if (options.max !== undefined) { schema = schema.max(options.max); } + if (options.unsafe === true) { + schema = schema.unsafe(true); + } super(schema, options); } diff --git a/packages/kbn-crypto-browser/BUILD.bazel b/packages/kbn-crypto-browser/BUILD.bazel new file mode 100644 index 0000000000000..c959d62ffde68 --- /dev/null +++ b/packages/kbn-crypto-browser/BUILD.bazel @@ -0,0 +1,32 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") + +SRCS = glob( + [ + "**/*.ts", + ], + exclude = [ + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +BUNDLER_DEPS = [ + "@npm//tslib", +] + +js_library( + name = "kbn-crypto-browser", + package_name = "@kbn/crypto-browser", + srcs = ["package.json"] + SRCS, + deps = BUNDLER_DEPS, + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-data-view-utils/index.ts b/packages/kbn-data-view-utils/index.ts index 1c881dbdacf79..ad783bc163c59 100644 --- a/packages/kbn-data-view-utils/index.ts +++ b/packages/kbn-data-view-utils/index.ts @@ -7,6 +7,6 @@ */ export * from './src/constants'; - +export { convertDatatableColumnToDataViewFieldSpec } from './src/utils/convert_to_data_view_field_spec'; export { createRegExpPatternFrom } from './src/utils/create_regexp_pattern_from'; export { testPatternAgainstAllowedList } from './src/utils/test_pattern_against_allowed_list'; diff --git a/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.test.ts b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.test.ts new file mode 100644 index 0000000000000..94dce4a6a60da --- /dev/null +++ b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.test.ts @@ -0,0 +1,60 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { DatatableColumnType } from '@kbn/expressions-plugin/common'; +import { convertDatatableColumnToDataViewFieldSpec } from './convert_to_data_view_field_spec'; + +describe('convertDatatableColumnToDataViewFieldSpec', () => { + it('should return a DataViewField object for a counter column', () => { + const column = { + id: 'bytes_counter', + name: 'bytes_counter', + meta: { + esType: 'counter_long', + type: 'number' as DatatableColumnType, + }, + isNull: false, + }; + const result = convertDatatableColumnToDataViewFieldSpec(column); + expect(result).toEqual( + expect.objectContaining({ + name: 'bytes_counter', + type: 'number', + esTypes: ['long'], + searchable: true, + aggregatable: false, + isNull: false, + timeSeriesMetric: 'counter', + }) + ); + }); + + it('should return a DataViewField object with timeSeriesMetric undefined if esType does not start with counter_', () => { + const column = { + id: 'test', + name: 'test', + meta: { + esType: 'keyword', + type: 'string' as DatatableColumnType, + }, + isNull: false, + }; + const result = convertDatatableColumnToDataViewFieldSpec(column); + expect(result.timeSeriesMetric).toBeUndefined(); + expect(result).toEqual( + expect.objectContaining({ + name: 'test', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: false, + isNull: false, + }) + ); + }); +}); diff --git a/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts new file mode 100644 index 0000000000000..7cc408a414ade --- /dev/null +++ b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts @@ -0,0 +1,37 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import type { DatatableColumn } from '@kbn/expressions-plugin/common'; +import type { MappingTimeSeriesMetricType } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { FieldSpec } from '@kbn/data-views-plugin/common'; + +/** + * Convert a datatable column to a DataViewFieldSpec + */ +export function convertDatatableColumnToDataViewFieldSpec(column: DatatableColumn): FieldSpec { + let esType = column.meta?.esType; + let timeSeriesMetric: MappingTimeSeriesMetricType | undefined; + + // 'counter_integer', 'counter_long', 'counter_double'... + if (esType?.startsWith('counter_')) { + esType = esType?.replace('counter_', ''); + timeSeriesMetric = 'counter'; + } + + // `DataViewField` class is defined in "data-views" plugin, so we can't create an instance of it from a package. + // We will return a data view field spec here instead then. + return { + name: column.name, + type: column.meta?.type ?? 'unknown', + esTypes: esType ? [esType] : undefined, + searchable: true, + aggregatable: false, + isNull: Boolean(column?.isNull), + ...(timeSeriesMetric ? { timeSeriesMetric } : {}), + }; +} diff --git a/packages/kbn-data-view-utils/tsconfig.json b/packages/kbn-data-view-utils/tsconfig.json index a41af0b7c5017..05400030e1001 100644 --- a/packages/kbn-data-view-utils/tsconfig.json +++ b/packages/kbn-data-view-utils/tsconfig.json @@ -14,5 +14,9 @@ ], "exclude": [ "target/**/*" + ], + "kbn_references": [ + "@kbn/data-views-plugin", + "@kbn/expressions-plugin", ] } diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index ba74061778b6d..9b38a716a714c 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -488,8 +488,6 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D ruleUiAdvancedParams: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html#rule-ui-advanced-params`, entityAnalytics: { riskScorePrerequisites: `${SECURITY_SOLUTION_DOCS}ers-requirements.html`, - hostRiskScore: `${SECURITY_SOLUTION_DOCS}host-risk-score.html`, - userRiskScore: `${SECURITY_SOLUTION_DOCS}user-risk-score.html`, entityRiskScoring: `${SECURITY_SOLUTION_DOCS}entity-risk-scoring.html`, assetCriticality: `${SECURITY_SOLUTION_DOCS}asset-criticality.html`, }, @@ -522,6 +520,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D rollupSettings: `${KIBANA_DOCS}advanced-options.html#kibana-rollups-settings`, visualizationSettings: `${KIBANA_DOCS}advanced-options.html#kibana-visualization-settings`, timelionSettings: `${KIBANA_DOCS}advanced-options.html#kibana-timelion-settings`, + generalSettings: `${KIBANA_DOCS}advanced-options.html#kibana-general-settings`, savedObjectsApiList: `${KIBANA_DOCS}saved-objects-api.html#saved-objects-api`, }, ml: { diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 5586f0f8f201f..c6b1b753ce883 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -360,8 +360,6 @@ export interface DocLinks { readonly ruleUiAdvancedParams: string; readonly entityAnalytics: { readonly riskScorePrerequisites: string; - readonly hostRiskScore: string; - readonly userRiskScore: string; readonly entityRiskScoring: string; readonly assetCriticality: string; }; diff --git a/packages/kbn-esql-ast/index.ts b/packages/kbn-esql-ast/index.ts index fc129c5c6fac3..6c8cd3c23e50b 100644 --- a/packages/kbn-esql-ast/index.ts +++ b/packages/kbn-esql-ast/index.ts @@ -22,6 +22,7 @@ export type { ESQLSource, ESQLColumn, ESQLLiteral, + ESQLParamLiteral, AstProviderFn, EditorError, ESQLAstNode, diff --git a/packages/kbn-esql-ast/src/__tests__/ast_parser.from.test.ts b/packages/kbn-esql-ast/src/__tests__/ast_parser.from.test.ts index 00559da4fff9c..a88b51f63ea16 100644 --- a/packages/kbn-esql-ast/src/__tests__/ast_parser.from.test.ts +++ b/packages/kbn-esql-ast/src/__tests__/ast_parser.from.test.ts @@ -60,6 +60,36 @@ describe('FROM', () => { ]); }); + it('can parse FROM query with single quote or triple quote', () => { + const text = '\tFROM "foo%" \t\t, """bar{{00-00}}""", \n baz'; + const { ast, errors } = parse(text); + + expect(errors.length).toBe(0); + expect(ast).toMatchObject([ + { + type: 'command', + name: 'from', + args: [ + { + type: 'source', + name: 'foo%', + sourceType: 'index', + }, + { + type: 'source', + name: 'bar{{00-00}}', + sourceType: 'index', + }, + { + type: 'source', + name: 'baz', + sourceType: 'index', + }, + ], + }, + ]); + }); + it('can parse FROM query with a single metadata column', () => { const text = 'from foo METADATA bar'; const { ast, errors } = parse(text); diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 index d7a4b88e6d5c5..12a47a195dfda 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 @@ -44,13 +44,15 @@ WS : [ \r\n\t]+ -> channel(HIDDEN) ; -fragment INDEX_UNQUOTED_IDENTIFIER_PART - : ~[=`|,[\]/ \t\r\n] - | '/' ~[*/] // allow single / but not followed by another / or * which would start a comment +// in 8.14 ` were not allowed +// this has been relaxed in 8.15 since " is used for quoting +fragment UNQUOTED_SOURCE_PART + : ~[:"=|,[\]/ \t\r\n] + | '/' ~[*/] // allow single / but not followed by another / or * which would start a comment -- used in index pattern date spec ; -INDEX_UNQUOTED_IDENTIFIER - : INDEX_UNQUOTED_IDENTIFIER_PART+ +UNQUOTED_SOURCE + : UNQUOTED_SOURCE_PART+ ; // @@ -212,15 +214,13 @@ mode FROM_MODE; FROM_PIPE : PIPE -> type(PIPE), popMode; FROM_OPENING_BRACKET : OPENING_BRACKET -> type(OPENING_BRACKET); FROM_CLOSING_BRACKET : CLOSING_BRACKET -> type(CLOSING_BRACKET); +FROM_COLON : COLON -> type(COLON); FROM_COMMA : COMMA -> type(COMMA); FROM_ASSIGN : ASSIGN -> type(ASSIGN); -FROM_QUOTED_STRING : QUOTED_STRING -> type(QUOTED_STRING); - METADATA : 'metadata'; -FROM_INDEX_UNQUOTED_IDENTIFIER - : INDEX_UNQUOTED_IDENTIFIER -> type(INDEX_UNQUOTED_IDENTIFIER) - ; +FROM_UNQUOTED_SOURCE : UNQUOTED_SOURCE -> type(UNQUOTED_SOURCE); +FROM_QUOTED_SOURCE : QUOTED_STRING -> type(QUOTED_STRING); FROM_LINE_COMMENT : LINE_COMMENT -> channel(HIDDEN) @@ -311,10 +311,6 @@ ENRICH_POLICY_NAME : (ENRICH_POLICY_NAME_BODY+ COLON)? ENRICH_POLICY_NAME_BODY+ ; -ENRICH_QUOTED_IDENTIFIER - : QUOTED_IDENTIFIER -> type(QUOTED_IDENTIFIER) - ; - ENRICH_MODE_UNQUOTED_VALUE : ENRICH_POLICY_NAME -> type(ENRICH_POLICY_NAME) ; @@ -331,7 +327,7 @@ ENRICH_WS : WS -> channel(HIDDEN) ; -// submode for Enrich to allow different lexing between policy identifier (loose) and field identifiers +// submode for Enrich to allow different lexing between policy source (loose) and field identifiers mode ENRICH_FIELD_MODE; ENRICH_FIELD_PIPE : PIPE -> type(PIPE), popMode, popMode; ENRICH_FIELD_ASSIGN : ASSIGN -> type(ASSIGN); @@ -363,13 +359,13 @@ ENRICH_FIELD_WS // LOOKUP ON key mode LOOKUP_MODE; LOOKUP_PIPE : PIPE -> type(PIPE), popMode; +LOOKUP_COLON : COLON -> type(COLON); LOOKUP_COMMA : COMMA -> type(COMMA); LOOKUP_DOT: DOT -> type(DOT); LOOKUP_ON : ON -> type(ON), pushMode(LOOKUP_FIELD_MODE); -LOOKUP_INDEX_UNQUOTED_IDENTIFIER - : INDEX_UNQUOTED_IDENTIFIER -> type(INDEX_UNQUOTED_IDENTIFIER) - ; +LOOKUP_UNQUOTED_SOURCE: UNQUOTED_SOURCE -> type(UNQUOTED_SOURCE); +LOOKUP_QUOTED_SOURCE : QUOTED_STRING -> type(QUOTED_STRING); LOOKUP_LINE_COMMENT : LINE_COMMENT -> channel(HIDDEN) @@ -496,9 +492,8 @@ SETTING_WS mode METRICS_MODE; METRICS_PIPE : PIPE -> type(PIPE), popMode; -METRICS_INDEX_UNQUOTED_IDENTIFIER - : INDEX_UNQUOTED_IDENTIFIER -> type(INDEX_UNQUOTED_IDENTIFIER), popMode, pushMode(CLOSING_METRICS_MODE) - ; +METRICS_UNQUOTED_SOURCE: UNQUOTED_SOURCE -> type(UNQUOTED_SOURCE), popMode, pushMode(CLOSING_METRICS_MODE); +METRICS_QUOTED_SOURCE : QUOTED_STRING -> type(QUOTED_STRING), popMode, pushMode(CLOSING_METRICS_MODE); METRICS_LINE_COMMENT : LINE_COMMENT -> channel(HIDDEN) @@ -515,6 +510,10 @@ METRICS_WS // TODO: remove this workaround mode - see https://github.com/elastic/elasticsearch/issues/108528 mode CLOSING_METRICS_MODE; +CLOSING_METRICS_COLON + : COLON -> type(COLON), popMode, pushMode(METRICS_MODE) + ; + CLOSING_METRICS_COMMA : COMMA -> type(COMMA), popMode, pushMode(METRICS_MODE) ; diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp index 911e1371fd129..f0826dc49df6e 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp @@ -151,7 +151,7 @@ UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS -INDEX_UNQUOTED_IDENTIFIER +UNQUOTED_SOURCE EXPLAIN_WS EXPLAIN_LINE_COMMENT EXPLAIN_MULTILINE_COMMENT @@ -277,8 +277,8 @@ UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS -INDEX_UNQUOTED_IDENTIFIER_PART -INDEX_UNQUOTED_IDENTIFIER +UNQUOTED_SOURCE_PART +UNQUOTED_SOURCE EXPLAIN_OPENING_BRACKET EXPLAIN_PIPE EXPLAIN_WS @@ -345,11 +345,12 @@ EXPR_WS FROM_PIPE FROM_OPENING_BRACKET FROM_CLOSING_BRACKET +FROM_COLON FROM_COMMA FROM_ASSIGN -FROM_QUOTED_STRING METADATA -FROM_INDEX_UNQUOTED_IDENTIFIER +FROM_UNQUOTED_SOURCE +FROM_QUOTED_SOURCE FROM_LINE_COMMENT FROM_MULTILINE_COMMENT FROM_WS @@ -377,7 +378,6 @@ ON WITH ENRICH_POLICY_NAME_BODY ENRICH_POLICY_NAME -ENRICH_QUOTED_IDENTIFIER ENRICH_MODE_UNQUOTED_VALUE ENRICH_LINE_COMMENT ENRICH_MULTILINE_COMMENT @@ -393,10 +393,12 @@ ENRICH_FIELD_LINE_COMMENT ENRICH_FIELD_MULTILINE_COMMENT ENRICH_FIELD_WS LOOKUP_PIPE +LOOKUP_COLON LOOKUP_COMMA LOOKUP_DOT LOOKUP_ON -LOOKUP_INDEX_UNQUOTED_IDENTIFIER +LOOKUP_UNQUOTED_SOURCE +LOOKUP_QUOTED_SOURCE LOOKUP_LINE_COMMENT LOOKUP_MULTILINE_COMMENT LOOKUP_WS @@ -431,10 +433,12 @@ SETTING_LINE_COMMENT SETTTING_MULTILINE_COMMENT SETTING_WS METRICS_PIPE -METRICS_INDEX_UNQUOTED_IDENTIFIER +METRICS_UNQUOTED_SOURCE +METRICS_QUOTED_SOURCE METRICS_LINE_COMMENT METRICS_MULTILINE_COMMENT METRICS_WS +CLOSING_METRICS_COLON CLOSING_METRICS_COMMA CLOSING_METRICS_LINE_COMMENT CLOSING_METRICS_MULTILINE_COMMENT @@ -467,4 +471,4 @@ METRICS_MODE CLOSING_METRICS_MODE atn: -[4, 0, 124, 1422, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 4, 20, 567, 8, 20, 11, 20, 12, 20, 568, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 577, 8, 21, 10, 21, 12, 21, 580, 9, 21, 1, 21, 3, 21, 583, 8, 21, 1, 21, 3, 21, 586, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 5, 22, 595, 8, 22, 10, 22, 12, 22, 598, 9, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 4, 23, 606, 8, 23, 11, 23, 12, 23, 607, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 3, 24, 615, 8, 24, 1, 25, 4, 25, 618, 8, 25, 11, 25, 12, 25, 619, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 3, 36, 659, 8, 36, 1, 36, 4, 36, 662, 8, 36, 11, 36, 12, 36, 663, 1, 37, 1, 37, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 3, 39, 673, 8, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 3, 41, 680, 8, 41, 1, 42, 1, 42, 1, 42, 5, 42, 685, 8, 42, 10, 42, 12, 42, 688, 9, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 696, 8, 42, 10, 42, 12, 42, 699, 9, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 706, 8, 42, 1, 42, 3, 42, 709, 8, 42, 3, 42, 711, 8, 42, 1, 43, 4, 43, 714, 8, 43, 11, 43, 12, 43, 715, 1, 44, 4, 44, 719, 8, 44, 11, 44, 12, 44, 720, 1, 44, 1, 44, 5, 44, 725, 8, 44, 10, 44, 12, 44, 728, 9, 44, 1, 44, 1, 44, 4, 44, 732, 8, 44, 11, 44, 12, 44, 733, 1, 44, 4, 44, 737, 8, 44, 11, 44, 12, 44, 738, 1, 44, 1, 44, 5, 44, 743, 8, 44, 10, 44, 12, 44, 746, 9, 44, 3, 44, 748, 8, 44, 1, 44, 1, 44, 1, 44, 1, 44, 4, 44, 754, 8, 44, 11, 44, 12, 44, 755, 1, 44, 1, 44, 3, 44, 760, 8, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 5, 80, 882, 8, 80, 10, 80, 12, 80, 885, 9, 80, 1, 80, 1, 80, 4, 80, 889, 8, 80, 11, 80, 12, 80, 890, 3, 80, 893, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 5, 83, 907, 8, 83, 10, 83, 12, 83, 910, 9, 83, 1, 83, 1, 83, 3, 83, 914, 8, 83, 1, 83, 4, 83, 917, 8, 83, 11, 83, 12, 83, 918, 3, 83, 921, 8, 83, 1, 84, 1, 84, 4, 84, 925, 8, 84, 11, 84, 12, 84, 926, 1, 84, 1, 84, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 3, 103, 1012, 8, 103, 1, 104, 1, 104, 3, 104, 1016, 8, 104, 1, 104, 5, 104, 1019, 8, 104, 10, 104, 12, 104, 1022, 9, 104, 1, 104, 1, 104, 3, 104, 1026, 8, 104, 1, 104, 4, 104, 1029, 8, 104, 11, 104, 12, 104, 1030, 3, 104, 1033, 8, 104, 1, 105, 1, 105, 4, 105, 1037, 8, 105, 11, 105, 12, 105, 1038, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 123, 4, 123, 1114, 8, 123, 11, 123, 12, 123, 1115, 1, 123, 1, 123, 3, 123, 1120, 8, 123, 1, 123, 4, 123, 1123, 8, 123, 11, 123, 12, 123, 1124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 173, 4, 173, 1343, 8, 173, 11, 173, 12, 173, 1344, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 2, 596, 697, 0, 190, 16, 1, 18, 2, 20, 3, 22, 4, 24, 5, 26, 6, 28, 7, 30, 8, 32, 9, 34, 10, 36, 11, 38, 12, 40, 13, 42, 14, 44, 15, 46, 16, 48, 17, 50, 18, 52, 19, 54, 20, 56, 21, 58, 22, 60, 23, 62, 24, 64, 0, 66, 25, 68, 0, 70, 0, 72, 26, 74, 27, 76, 28, 78, 29, 80, 0, 82, 0, 84, 0, 86, 0, 88, 0, 90, 0, 92, 0, 94, 0, 96, 0, 98, 0, 100, 30, 102, 31, 104, 32, 106, 33, 108, 34, 110, 35, 112, 36, 114, 37, 116, 38, 118, 39, 120, 40, 122, 41, 124, 42, 126, 43, 128, 44, 130, 45, 132, 46, 134, 47, 136, 48, 138, 49, 140, 50, 142, 51, 144, 52, 146, 53, 148, 54, 150, 55, 152, 56, 154, 57, 156, 58, 158, 59, 160, 60, 162, 61, 164, 62, 166, 63, 168, 64, 170, 65, 172, 66, 174, 67, 176, 68, 178, 69, 180, 70, 182, 71, 184, 0, 186, 72, 188, 73, 190, 74, 192, 75, 194, 0, 196, 0, 198, 0, 200, 0, 202, 0, 204, 0, 206, 76, 208, 0, 210, 77, 212, 78, 214, 79, 216, 0, 218, 0, 220, 0, 222, 0, 224, 0, 226, 80, 228, 81, 230, 82, 232, 83, 234, 0, 236, 0, 238, 0, 240, 0, 242, 84, 244, 0, 246, 85, 248, 86, 250, 87, 252, 0, 254, 0, 256, 88, 258, 89, 260, 0, 262, 90, 264, 0, 266, 0, 268, 91, 270, 92, 272, 93, 274, 0, 276, 0, 278, 0, 280, 0, 282, 0, 284, 0, 286, 0, 288, 94, 290, 95, 292, 96, 294, 0, 296, 0, 298, 0, 300, 0, 302, 0, 304, 97, 306, 98, 308, 99, 310, 0, 312, 0, 314, 0, 316, 0, 318, 100, 320, 101, 322, 102, 324, 0, 326, 0, 328, 0, 330, 0, 332, 103, 334, 104, 336, 105, 338, 0, 340, 106, 342, 107, 344, 108, 346, 109, 348, 0, 350, 110, 352, 111, 354, 112, 356, 113, 358, 0, 360, 114, 362, 115, 364, 116, 366, 117, 368, 118, 370, 0, 372, 0, 374, 119, 376, 120, 378, 121, 380, 0, 382, 122, 384, 123, 386, 124, 388, 0, 390, 0, 392, 0, 394, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 85, 85, 117, 117, 2, 0, 87, 87, 119, 119, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 10, 0, 9, 10, 13, 13, 32, 32, 44, 44, 47, 47, 61, 61, 91, 91, 93, 93, 96, 96, 124, 124, 2, 0, 42, 42, 47, 47, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1448, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 0, 56, 1, 0, 0, 0, 0, 58, 1, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 62, 1, 0, 0, 0, 0, 66, 1, 0, 0, 0, 1, 68, 1, 0, 0, 0, 1, 70, 1, 0, 0, 0, 1, 72, 1, 0, 0, 0, 1, 74, 1, 0, 0, 0, 1, 76, 1, 0, 0, 0, 2, 78, 1, 0, 0, 0, 2, 100, 1, 0, 0, 0, 2, 102, 1, 0, 0, 0, 2, 104, 1, 0, 0, 0, 2, 106, 1, 0, 0, 0, 2, 108, 1, 0, 0, 0, 2, 110, 1, 0, 0, 0, 2, 112, 1, 0, 0, 0, 2, 114, 1, 0, 0, 0, 2, 116, 1, 0, 0, 0, 2, 118, 1, 0, 0, 0, 2, 120, 1, 0, 0, 0, 2, 122, 1, 0, 0, 0, 2, 124, 1, 0, 0, 0, 2, 126, 1, 0, 0, 0, 2, 128, 1, 0, 0, 0, 2, 130, 1, 0, 0, 0, 2, 132, 1, 0, 0, 0, 2, 134, 1, 0, 0, 0, 2, 136, 1, 0, 0, 0, 2, 138, 1, 0, 0, 0, 2, 140, 1, 0, 0, 0, 2, 142, 1, 0, 0, 0, 2, 144, 1, 0, 0, 0, 2, 146, 1, 0, 0, 0, 2, 148, 1, 0, 0, 0, 2, 150, 1, 0, 0, 0, 2, 152, 1, 0, 0, 0, 2, 154, 1, 0, 0, 0, 2, 156, 1, 0, 0, 0, 2, 158, 1, 0, 0, 0, 2, 160, 1, 0, 0, 0, 2, 162, 1, 0, 0, 0, 2, 164, 1, 0, 0, 0, 2, 166, 1, 0, 0, 0, 2, 168, 1, 0, 0, 0, 2, 170, 1, 0, 0, 0, 2, 172, 1, 0, 0, 0, 2, 174, 1, 0, 0, 0, 2, 176, 1, 0, 0, 0, 2, 178, 1, 0, 0, 0, 2, 180, 1, 0, 0, 0, 2, 182, 1, 0, 0, 0, 2, 186, 1, 0, 0, 0, 2, 188, 1, 0, 0, 0, 2, 190, 1, 0, 0, 0, 2, 192, 1, 0, 0, 0, 3, 194, 1, 0, 0, 0, 3, 196, 1, 0, 0, 0, 3, 198, 1, 0, 0, 0, 3, 200, 1, 0, 0, 0, 3, 202, 1, 0, 0, 0, 3, 204, 1, 0, 0, 0, 3, 206, 1, 0, 0, 0, 3, 208, 1, 0, 0, 0, 3, 210, 1, 0, 0, 0, 3, 212, 1, 0, 0, 0, 3, 214, 1, 0, 0, 0, 4, 216, 1, 0, 0, 0, 4, 218, 1, 0, 0, 0, 4, 220, 1, 0, 0, 0, 4, 226, 1, 0, 0, 0, 4, 228, 1, 0, 0, 0, 4, 230, 1, 0, 0, 0, 4, 232, 1, 0, 0, 0, 5, 234, 1, 0, 0, 0, 5, 236, 1, 0, 0, 0, 5, 238, 1, 0, 0, 0, 5, 240, 1, 0, 0, 0, 5, 242, 1, 0, 0, 0, 5, 244, 1, 0, 0, 0, 5, 246, 1, 0, 0, 0, 5, 248, 1, 0, 0, 0, 5, 250, 1, 0, 0, 0, 6, 252, 1, 0, 0, 0, 6, 254, 1, 0, 0, 0, 6, 256, 1, 0, 0, 0, 6, 258, 1, 0, 0, 0, 6, 262, 1, 0, 0, 0, 6, 264, 1, 0, 0, 0, 6, 266, 1, 0, 0, 0, 6, 268, 1, 0, 0, 0, 6, 270, 1, 0, 0, 0, 6, 272, 1, 0, 0, 0, 7, 274, 1, 0, 0, 0, 7, 276, 1, 0, 0, 0, 7, 278, 1, 0, 0, 0, 7, 280, 1, 0, 0, 0, 7, 282, 1, 0, 0, 0, 7, 284, 1, 0, 0, 0, 7, 286, 1, 0, 0, 0, 7, 288, 1, 0, 0, 0, 7, 290, 1, 0, 0, 0, 7, 292, 1, 0, 0, 0, 8, 294, 1, 0, 0, 0, 8, 296, 1, 0, 0, 0, 8, 298, 1, 0, 0, 0, 8, 300, 1, 0, 0, 0, 8, 302, 1, 0, 0, 0, 8, 304, 1, 0, 0, 0, 8, 306, 1, 0, 0, 0, 8, 308, 1, 0, 0, 0, 9, 310, 1, 0, 0, 0, 9, 312, 1, 0, 0, 0, 9, 314, 1, 0, 0, 0, 9, 316, 1, 0, 0, 0, 9, 318, 1, 0, 0, 0, 9, 320, 1, 0, 0, 0, 9, 322, 1, 0, 0, 0, 10, 324, 1, 0, 0, 0, 10, 326, 1, 0, 0, 0, 10, 328, 1, 0, 0, 0, 10, 330, 1, 0, 0, 0, 10, 332, 1, 0, 0, 0, 10, 334, 1, 0, 0, 0, 10, 336, 1, 0, 0, 0, 11, 338, 1, 0, 0, 0, 11, 340, 1, 0, 0, 0, 11, 342, 1, 0, 0, 0, 11, 344, 1, 0, 0, 0, 11, 346, 1, 0, 0, 0, 12, 348, 1, 0, 0, 0, 12, 350, 1, 0, 0, 0, 12, 352, 1, 0, 0, 0, 12, 354, 1, 0, 0, 0, 12, 356, 1, 0, 0, 0, 13, 358, 1, 0, 0, 0, 13, 360, 1, 0, 0, 0, 13, 362, 1, 0, 0, 0, 13, 364, 1, 0, 0, 0, 13, 366, 1, 0, 0, 0, 13, 368, 1, 0, 0, 0, 14, 370, 1, 0, 0, 0, 14, 372, 1, 0, 0, 0, 14, 374, 1, 0, 0, 0, 14, 376, 1, 0, 0, 0, 14, 378, 1, 0, 0, 0, 15, 380, 1, 0, 0, 0, 15, 382, 1, 0, 0, 0, 15, 384, 1, 0, 0, 0, 15, 386, 1, 0, 0, 0, 15, 388, 1, 0, 0, 0, 15, 390, 1, 0, 0, 0, 15, 392, 1, 0, 0, 0, 15, 394, 1, 0, 0, 0, 16, 396, 1, 0, 0, 0, 18, 406, 1, 0, 0, 0, 20, 413, 1, 0, 0, 0, 22, 422, 1, 0, 0, 0, 24, 429, 1, 0, 0, 0, 26, 439, 1, 0, 0, 0, 28, 446, 1, 0, 0, 0, 30, 453, 1, 0, 0, 0, 32, 467, 1, 0, 0, 0, 34, 474, 1, 0, 0, 0, 36, 482, 1, 0, 0, 0, 38, 491, 1, 0, 0, 0, 40, 498, 1, 0, 0, 0, 42, 508, 1, 0, 0, 0, 44, 520, 1, 0, 0, 0, 46, 529, 1, 0, 0, 0, 48, 535, 1, 0, 0, 0, 50, 542, 1, 0, 0, 0, 52, 549, 1, 0, 0, 0, 54, 557, 1, 0, 0, 0, 56, 566, 1, 0, 0, 0, 58, 572, 1, 0, 0, 0, 60, 589, 1, 0, 0, 0, 62, 605, 1, 0, 0, 0, 64, 614, 1, 0, 0, 0, 66, 617, 1, 0, 0, 0, 68, 621, 1, 0, 0, 0, 70, 626, 1, 0, 0, 0, 72, 631, 1, 0, 0, 0, 74, 635, 1, 0, 0, 0, 76, 639, 1, 0, 0, 0, 78, 643, 1, 0, 0, 0, 80, 647, 1, 0, 0, 0, 82, 649, 1, 0, 0, 0, 84, 651, 1, 0, 0, 0, 86, 654, 1, 0, 0, 0, 88, 656, 1, 0, 0, 0, 90, 665, 1, 0, 0, 0, 92, 667, 1, 0, 0, 0, 94, 672, 1, 0, 0, 0, 96, 674, 1, 0, 0, 0, 98, 679, 1, 0, 0, 0, 100, 710, 1, 0, 0, 0, 102, 713, 1, 0, 0, 0, 104, 759, 1, 0, 0, 0, 106, 761, 1, 0, 0, 0, 108, 764, 1, 0, 0, 0, 110, 768, 1, 0, 0, 0, 112, 772, 1, 0, 0, 0, 114, 774, 1, 0, 0, 0, 116, 777, 1, 0, 0, 0, 118, 779, 1, 0, 0, 0, 120, 784, 1, 0, 0, 0, 122, 786, 1, 0, 0, 0, 124, 792, 1, 0, 0, 0, 126, 798, 1, 0, 0, 0, 128, 803, 1, 0, 0, 0, 130, 805, 1, 0, 0, 0, 132, 808, 1, 0, 0, 0, 134, 811, 1, 0, 0, 0, 136, 816, 1, 0, 0, 0, 138, 820, 1, 0, 0, 0, 140, 825, 1, 0, 0, 0, 142, 831, 1, 0, 0, 0, 144, 834, 1, 0, 0, 0, 146, 836, 1, 0, 0, 0, 148, 842, 1, 0, 0, 0, 150, 844, 1, 0, 0, 0, 152, 849, 1, 0, 0, 0, 154, 852, 1, 0, 0, 0, 156, 855, 1, 0, 0, 0, 158, 858, 1, 0, 0, 0, 160, 860, 1, 0, 0, 0, 162, 863, 1, 0, 0, 0, 164, 865, 1, 0, 0, 0, 166, 868, 1, 0, 0, 0, 168, 870, 1, 0, 0, 0, 170, 872, 1, 0, 0, 0, 172, 874, 1, 0, 0, 0, 174, 876, 1, 0, 0, 0, 176, 892, 1, 0, 0, 0, 178, 894, 1, 0, 0, 0, 180, 899, 1, 0, 0, 0, 182, 920, 1, 0, 0, 0, 184, 922, 1, 0, 0, 0, 186, 930, 1, 0, 0, 0, 188, 932, 1, 0, 0, 0, 190, 936, 1, 0, 0, 0, 192, 940, 1, 0, 0, 0, 194, 944, 1, 0, 0, 0, 196, 949, 1, 0, 0, 0, 198, 953, 1, 0, 0, 0, 200, 957, 1, 0, 0, 0, 202, 961, 1, 0, 0, 0, 204, 965, 1, 0, 0, 0, 206, 969, 1, 0, 0, 0, 208, 978, 1, 0, 0, 0, 210, 982, 1, 0, 0, 0, 212, 986, 1, 0, 0, 0, 214, 990, 1, 0, 0, 0, 216, 994, 1, 0, 0, 0, 218, 999, 1, 0, 0, 0, 220, 1003, 1, 0, 0, 0, 222, 1011, 1, 0, 0, 0, 224, 1032, 1, 0, 0, 0, 226, 1036, 1, 0, 0, 0, 228, 1040, 1, 0, 0, 0, 230, 1044, 1, 0, 0, 0, 232, 1048, 1, 0, 0, 0, 234, 1052, 1, 0, 0, 0, 236, 1057, 1, 0, 0, 0, 238, 1061, 1, 0, 0, 0, 240, 1065, 1, 0, 0, 0, 242, 1069, 1, 0, 0, 0, 244, 1072, 1, 0, 0, 0, 246, 1076, 1, 0, 0, 0, 248, 1080, 1, 0, 0, 0, 250, 1084, 1, 0, 0, 0, 252, 1088, 1, 0, 0, 0, 254, 1093, 1, 0, 0, 0, 256, 1098, 1, 0, 0, 0, 258, 1103, 1, 0, 0, 0, 260, 1110, 1, 0, 0, 0, 262, 1119, 1, 0, 0, 0, 264, 1126, 1, 0, 0, 0, 266, 1130, 1, 0, 0, 0, 268, 1134, 1, 0, 0, 0, 270, 1138, 1, 0, 0, 0, 272, 1142, 1, 0, 0, 0, 274, 1146, 1, 0, 0, 0, 276, 1152, 1, 0, 0, 0, 278, 1156, 1, 0, 0, 0, 280, 1160, 1, 0, 0, 0, 282, 1164, 1, 0, 0, 0, 284, 1168, 1, 0, 0, 0, 286, 1172, 1, 0, 0, 0, 288, 1176, 1, 0, 0, 0, 290, 1180, 1, 0, 0, 0, 292, 1184, 1, 0, 0, 0, 294, 1188, 1, 0, 0, 0, 296, 1193, 1, 0, 0, 0, 298, 1197, 1, 0, 0, 0, 300, 1201, 1, 0, 0, 0, 302, 1206, 1, 0, 0, 0, 304, 1210, 1, 0, 0, 0, 306, 1214, 1, 0, 0, 0, 308, 1218, 1, 0, 0, 0, 310, 1222, 1, 0, 0, 0, 312, 1228, 1, 0, 0, 0, 314, 1232, 1, 0, 0, 0, 316, 1236, 1, 0, 0, 0, 318, 1240, 1, 0, 0, 0, 320, 1244, 1, 0, 0, 0, 322, 1248, 1, 0, 0, 0, 324, 1252, 1, 0, 0, 0, 326, 1257, 1, 0, 0, 0, 328, 1261, 1, 0, 0, 0, 330, 1265, 1, 0, 0, 0, 332, 1269, 1, 0, 0, 0, 334, 1273, 1, 0, 0, 0, 336, 1277, 1, 0, 0, 0, 338, 1281, 1, 0, 0, 0, 340, 1286, 1, 0, 0, 0, 342, 1291, 1, 0, 0, 0, 344, 1295, 1, 0, 0, 0, 346, 1299, 1, 0, 0, 0, 348, 1303, 1, 0, 0, 0, 350, 1308, 1, 0, 0, 0, 352, 1318, 1, 0, 0, 0, 354, 1322, 1, 0, 0, 0, 356, 1326, 1, 0, 0, 0, 358, 1330, 1, 0, 0, 0, 360, 1335, 1, 0, 0, 0, 362, 1342, 1, 0, 0, 0, 364, 1346, 1, 0, 0, 0, 366, 1350, 1, 0, 0, 0, 368, 1354, 1, 0, 0, 0, 370, 1358, 1, 0, 0, 0, 372, 1363, 1, 0, 0, 0, 374, 1369, 1, 0, 0, 0, 376, 1373, 1, 0, 0, 0, 378, 1377, 1, 0, 0, 0, 380, 1381, 1, 0, 0, 0, 382, 1387, 1, 0, 0, 0, 384, 1391, 1, 0, 0, 0, 386, 1395, 1, 0, 0, 0, 388, 1399, 1, 0, 0, 0, 390, 1405, 1, 0, 0, 0, 392, 1411, 1, 0, 0, 0, 394, 1417, 1, 0, 0, 0, 396, 397, 7, 0, 0, 0, 397, 398, 7, 1, 0, 0, 398, 399, 7, 2, 0, 0, 399, 400, 7, 2, 0, 0, 400, 401, 7, 3, 0, 0, 401, 402, 7, 4, 0, 0, 402, 403, 7, 5, 0, 0, 403, 404, 1, 0, 0, 0, 404, 405, 6, 0, 0, 0, 405, 17, 1, 0, 0, 0, 406, 407, 7, 0, 0, 0, 407, 408, 7, 6, 0, 0, 408, 409, 7, 7, 0, 0, 409, 410, 7, 8, 0, 0, 410, 411, 1, 0, 0, 0, 411, 412, 6, 1, 1, 0, 412, 19, 1, 0, 0, 0, 413, 414, 7, 3, 0, 0, 414, 415, 7, 9, 0, 0, 415, 416, 7, 6, 0, 0, 416, 417, 7, 1, 0, 0, 417, 418, 7, 4, 0, 0, 418, 419, 7, 10, 0, 0, 419, 420, 1, 0, 0, 0, 420, 421, 6, 2, 2, 0, 421, 21, 1, 0, 0, 0, 422, 423, 7, 3, 0, 0, 423, 424, 7, 11, 0, 0, 424, 425, 7, 12, 0, 0, 425, 426, 7, 13, 0, 0, 426, 427, 1, 0, 0, 0, 427, 428, 6, 3, 0, 0, 428, 23, 1, 0, 0, 0, 429, 430, 7, 3, 0, 0, 430, 431, 7, 14, 0, 0, 431, 432, 7, 8, 0, 0, 432, 433, 7, 13, 0, 0, 433, 434, 7, 12, 0, 0, 434, 435, 7, 1, 0, 0, 435, 436, 7, 9, 0, 0, 436, 437, 1, 0, 0, 0, 437, 438, 6, 4, 3, 0, 438, 25, 1, 0, 0, 0, 439, 440, 7, 15, 0, 0, 440, 441, 7, 6, 0, 0, 441, 442, 7, 7, 0, 0, 442, 443, 7, 16, 0, 0, 443, 444, 1, 0, 0, 0, 444, 445, 6, 5, 4, 0, 445, 27, 1, 0, 0, 0, 446, 447, 7, 17, 0, 0, 447, 448, 7, 6, 0, 0, 448, 449, 7, 7, 0, 0, 449, 450, 7, 18, 0, 0, 450, 451, 1, 0, 0, 0, 451, 452, 6, 6, 0, 0, 452, 29, 1, 0, 0, 0, 453, 454, 7, 1, 0, 0, 454, 455, 7, 9, 0, 0, 455, 456, 7, 13, 0, 0, 456, 457, 7, 1, 0, 0, 457, 458, 7, 9, 0, 0, 458, 459, 7, 3, 0, 0, 459, 460, 7, 2, 0, 0, 460, 461, 7, 5, 0, 0, 461, 462, 7, 12, 0, 0, 462, 463, 7, 5, 0, 0, 463, 464, 7, 2, 0, 0, 464, 465, 1, 0, 0, 0, 465, 466, 6, 7, 0, 0, 466, 31, 1, 0, 0, 0, 467, 468, 7, 18, 0, 0, 468, 469, 7, 3, 0, 0, 469, 470, 7, 3, 0, 0, 470, 471, 7, 8, 0, 0, 471, 472, 1, 0, 0, 0, 472, 473, 6, 8, 1, 0, 473, 33, 1, 0, 0, 0, 474, 475, 7, 13, 0, 0, 475, 476, 7, 1, 0, 0, 476, 477, 7, 16, 0, 0, 477, 478, 7, 1, 0, 0, 478, 479, 7, 5, 0, 0, 479, 480, 1, 0, 0, 0, 480, 481, 6, 9, 0, 0, 481, 35, 1, 0, 0, 0, 482, 483, 7, 13, 0, 0, 483, 484, 7, 7, 0, 0, 484, 485, 7, 7, 0, 0, 485, 486, 7, 18, 0, 0, 486, 487, 7, 19, 0, 0, 487, 488, 7, 8, 0, 0, 488, 489, 1, 0, 0, 0, 489, 490, 6, 10, 5, 0, 490, 37, 1, 0, 0, 0, 491, 492, 7, 16, 0, 0, 492, 493, 7, 3, 0, 0, 493, 494, 7, 5, 0, 0, 494, 495, 7, 12, 0, 0, 495, 496, 1, 0, 0, 0, 496, 497, 6, 11, 6, 0, 497, 39, 1, 0, 0, 0, 498, 499, 7, 16, 0, 0, 499, 500, 7, 3, 0, 0, 500, 501, 7, 5, 0, 0, 501, 502, 7, 6, 0, 0, 502, 503, 7, 1, 0, 0, 503, 504, 7, 4, 0, 0, 504, 505, 7, 2, 0, 0, 505, 506, 1, 0, 0, 0, 506, 507, 6, 12, 7, 0, 507, 41, 1, 0, 0, 0, 508, 509, 7, 16, 0, 0, 509, 510, 7, 11, 0, 0, 510, 511, 5, 95, 0, 0, 511, 512, 7, 3, 0, 0, 512, 513, 7, 14, 0, 0, 513, 514, 7, 8, 0, 0, 514, 515, 7, 12, 0, 0, 515, 516, 7, 9, 0, 0, 516, 517, 7, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 519, 6, 13, 8, 0, 519, 43, 1, 0, 0, 0, 520, 521, 7, 6, 0, 0, 521, 522, 7, 3, 0, 0, 522, 523, 7, 9, 0, 0, 523, 524, 7, 12, 0, 0, 524, 525, 7, 16, 0, 0, 525, 526, 7, 3, 0, 0, 526, 527, 1, 0, 0, 0, 527, 528, 6, 14, 9, 0, 528, 45, 1, 0, 0, 0, 529, 530, 7, 6, 0, 0, 530, 531, 7, 7, 0, 0, 531, 532, 7, 20, 0, 0, 532, 533, 1, 0, 0, 0, 533, 534, 6, 15, 0, 0, 534, 47, 1, 0, 0, 0, 535, 536, 7, 2, 0, 0, 536, 537, 7, 10, 0, 0, 537, 538, 7, 7, 0, 0, 538, 539, 7, 20, 0, 0, 539, 540, 1, 0, 0, 0, 540, 541, 6, 16, 10, 0, 541, 49, 1, 0, 0, 0, 542, 543, 7, 2, 0, 0, 543, 544, 7, 7, 0, 0, 544, 545, 7, 6, 0, 0, 545, 546, 7, 5, 0, 0, 546, 547, 1, 0, 0, 0, 547, 548, 6, 17, 0, 0, 548, 51, 1, 0, 0, 0, 549, 550, 7, 2, 0, 0, 550, 551, 7, 5, 0, 0, 551, 552, 7, 12, 0, 0, 552, 553, 7, 5, 0, 0, 553, 554, 7, 2, 0, 0, 554, 555, 1, 0, 0, 0, 555, 556, 6, 18, 0, 0, 556, 53, 1, 0, 0, 0, 557, 558, 7, 20, 0, 0, 558, 559, 7, 10, 0, 0, 559, 560, 7, 3, 0, 0, 560, 561, 7, 6, 0, 0, 561, 562, 7, 3, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 6, 19, 0, 0, 564, 55, 1, 0, 0, 0, 565, 567, 8, 21, 0, 0, 566, 565, 1, 0, 0, 0, 567, 568, 1, 0, 0, 0, 568, 566, 1, 0, 0, 0, 568, 569, 1, 0, 0, 0, 569, 570, 1, 0, 0, 0, 570, 571, 6, 20, 0, 0, 571, 57, 1, 0, 0, 0, 572, 573, 5, 47, 0, 0, 573, 574, 5, 47, 0, 0, 574, 578, 1, 0, 0, 0, 575, 577, 8, 22, 0, 0, 576, 575, 1, 0, 0, 0, 577, 580, 1, 0, 0, 0, 578, 576, 1, 0, 0, 0, 578, 579, 1, 0, 0, 0, 579, 582, 1, 0, 0, 0, 580, 578, 1, 0, 0, 0, 581, 583, 5, 13, 0, 0, 582, 581, 1, 0, 0, 0, 582, 583, 1, 0, 0, 0, 583, 585, 1, 0, 0, 0, 584, 586, 5, 10, 0, 0, 585, 584, 1, 0, 0, 0, 585, 586, 1, 0, 0, 0, 586, 587, 1, 0, 0, 0, 587, 588, 6, 21, 11, 0, 588, 59, 1, 0, 0, 0, 589, 590, 5, 47, 0, 0, 590, 591, 5, 42, 0, 0, 591, 596, 1, 0, 0, 0, 592, 595, 3, 60, 22, 0, 593, 595, 9, 0, 0, 0, 594, 592, 1, 0, 0, 0, 594, 593, 1, 0, 0, 0, 595, 598, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 596, 594, 1, 0, 0, 0, 597, 599, 1, 0, 0, 0, 598, 596, 1, 0, 0, 0, 599, 600, 5, 42, 0, 0, 600, 601, 5, 47, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 6, 22, 11, 0, 603, 61, 1, 0, 0, 0, 604, 606, 7, 23, 0, 0, 605, 604, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 605, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 609, 1, 0, 0, 0, 609, 610, 6, 23, 11, 0, 610, 63, 1, 0, 0, 0, 611, 615, 8, 24, 0, 0, 612, 613, 5, 47, 0, 0, 613, 615, 8, 25, 0, 0, 614, 611, 1, 0, 0, 0, 614, 612, 1, 0, 0, 0, 615, 65, 1, 0, 0, 0, 616, 618, 3, 64, 24, 0, 617, 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 617, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 67, 1, 0, 0, 0, 621, 622, 3, 178, 81, 0, 622, 623, 1, 0, 0, 0, 623, 624, 6, 26, 12, 0, 624, 625, 6, 26, 13, 0, 625, 69, 1, 0, 0, 0, 626, 627, 3, 78, 31, 0, 627, 628, 1, 0, 0, 0, 628, 629, 6, 27, 14, 0, 629, 630, 6, 27, 15, 0, 630, 71, 1, 0, 0, 0, 631, 632, 3, 62, 23, 0, 632, 633, 1, 0, 0, 0, 633, 634, 6, 28, 11, 0, 634, 73, 1, 0, 0, 0, 635, 636, 3, 58, 21, 0, 636, 637, 1, 0, 0, 0, 637, 638, 6, 29, 11, 0, 638, 75, 1, 0, 0, 0, 639, 640, 3, 60, 22, 0, 640, 641, 1, 0, 0, 0, 641, 642, 6, 30, 11, 0, 642, 77, 1, 0, 0, 0, 643, 644, 5, 124, 0, 0, 644, 645, 1, 0, 0, 0, 645, 646, 6, 31, 15, 0, 646, 79, 1, 0, 0, 0, 647, 648, 7, 26, 0, 0, 648, 81, 1, 0, 0, 0, 649, 650, 7, 27, 0, 0, 650, 83, 1, 0, 0, 0, 651, 652, 5, 92, 0, 0, 652, 653, 7, 28, 0, 0, 653, 85, 1, 0, 0, 0, 654, 655, 8, 29, 0, 0, 655, 87, 1, 0, 0, 0, 656, 658, 7, 3, 0, 0, 657, 659, 7, 30, 0, 0, 658, 657, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 661, 1, 0, 0, 0, 660, 662, 3, 80, 32, 0, 661, 660, 1, 0, 0, 0, 662, 663, 1, 0, 0, 0, 663, 661, 1, 0, 0, 0, 663, 664, 1, 0, 0, 0, 664, 89, 1, 0, 0, 0, 665, 666, 5, 64, 0, 0, 666, 91, 1, 0, 0, 0, 667, 668, 5, 96, 0, 0, 668, 93, 1, 0, 0, 0, 669, 673, 8, 31, 0, 0, 670, 671, 5, 96, 0, 0, 671, 673, 5, 96, 0, 0, 672, 669, 1, 0, 0, 0, 672, 670, 1, 0, 0, 0, 673, 95, 1, 0, 0, 0, 674, 675, 5, 95, 0, 0, 675, 97, 1, 0, 0, 0, 676, 680, 3, 82, 33, 0, 677, 680, 3, 80, 32, 0, 678, 680, 3, 96, 40, 0, 679, 676, 1, 0, 0, 0, 679, 677, 1, 0, 0, 0, 679, 678, 1, 0, 0, 0, 680, 99, 1, 0, 0, 0, 681, 686, 5, 34, 0, 0, 682, 685, 3, 84, 34, 0, 683, 685, 3, 86, 35, 0, 684, 682, 1, 0, 0, 0, 684, 683, 1, 0, 0, 0, 685, 688, 1, 0, 0, 0, 686, 684, 1, 0, 0, 0, 686, 687, 1, 0, 0, 0, 687, 689, 1, 0, 0, 0, 688, 686, 1, 0, 0, 0, 689, 711, 5, 34, 0, 0, 690, 691, 5, 34, 0, 0, 691, 692, 5, 34, 0, 0, 692, 693, 5, 34, 0, 0, 693, 697, 1, 0, 0, 0, 694, 696, 8, 22, 0, 0, 695, 694, 1, 0, 0, 0, 696, 699, 1, 0, 0, 0, 697, 698, 1, 0, 0, 0, 697, 695, 1, 0, 0, 0, 698, 700, 1, 0, 0, 0, 699, 697, 1, 0, 0, 0, 700, 701, 5, 34, 0, 0, 701, 702, 5, 34, 0, 0, 702, 703, 5, 34, 0, 0, 703, 705, 1, 0, 0, 0, 704, 706, 5, 34, 0, 0, 705, 704, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 706, 708, 1, 0, 0, 0, 707, 709, 5, 34, 0, 0, 708, 707, 1, 0, 0, 0, 708, 709, 1, 0, 0, 0, 709, 711, 1, 0, 0, 0, 710, 681, 1, 0, 0, 0, 710, 690, 1, 0, 0, 0, 711, 101, 1, 0, 0, 0, 712, 714, 3, 80, 32, 0, 713, 712, 1, 0, 0, 0, 714, 715, 1, 0, 0, 0, 715, 713, 1, 0, 0, 0, 715, 716, 1, 0, 0, 0, 716, 103, 1, 0, 0, 0, 717, 719, 3, 80, 32, 0, 718, 717, 1, 0, 0, 0, 719, 720, 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 720, 721, 1, 0, 0, 0, 721, 722, 1, 0, 0, 0, 722, 726, 3, 120, 52, 0, 723, 725, 3, 80, 32, 0, 724, 723, 1, 0, 0, 0, 725, 728, 1, 0, 0, 0, 726, 724, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 760, 1, 0, 0, 0, 728, 726, 1, 0, 0, 0, 729, 731, 3, 120, 52, 0, 730, 732, 3, 80, 32, 0, 731, 730, 1, 0, 0, 0, 732, 733, 1, 0, 0, 0, 733, 731, 1, 0, 0, 0, 733, 734, 1, 0, 0, 0, 734, 760, 1, 0, 0, 0, 735, 737, 3, 80, 32, 0, 736, 735, 1, 0, 0, 0, 737, 738, 1, 0, 0, 0, 738, 736, 1, 0, 0, 0, 738, 739, 1, 0, 0, 0, 739, 747, 1, 0, 0, 0, 740, 744, 3, 120, 52, 0, 741, 743, 3, 80, 32, 0, 742, 741, 1, 0, 0, 0, 743, 746, 1, 0, 0, 0, 744, 742, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 748, 1, 0, 0, 0, 746, 744, 1, 0, 0, 0, 747, 740, 1, 0, 0, 0, 747, 748, 1, 0, 0, 0, 748, 749, 1, 0, 0, 0, 749, 750, 3, 88, 36, 0, 750, 760, 1, 0, 0, 0, 751, 753, 3, 120, 52, 0, 752, 754, 3, 80, 32, 0, 753, 752, 1, 0, 0, 0, 754, 755, 1, 0, 0, 0, 755, 753, 1, 0, 0, 0, 755, 756, 1, 0, 0, 0, 756, 757, 1, 0, 0, 0, 757, 758, 3, 88, 36, 0, 758, 760, 1, 0, 0, 0, 759, 718, 1, 0, 0, 0, 759, 729, 1, 0, 0, 0, 759, 736, 1, 0, 0, 0, 759, 751, 1, 0, 0, 0, 760, 105, 1, 0, 0, 0, 761, 762, 7, 32, 0, 0, 762, 763, 7, 33, 0, 0, 763, 107, 1, 0, 0, 0, 764, 765, 7, 12, 0, 0, 765, 766, 7, 9, 0, 0, 766, 767, 7, 0, 0, 0, 767, 109, 1, 0, 0, 0, 768, 769, 7, 12, 0, 0, 769, 770, 7, 2, 0, 0, 770, 771, 7, 4, 0, 0, 771, 111, 1, 0, 0, 0, 772, 773, 5, 61, 0, 0, 773, 113, 1, 0, 0, 0, 774, 775, 5, 58, 0, 0, 775, 776, 5, 58, 0, 0, 776, 115, 1, 0, 0, 0, 777, 778, 5, 44, 0, 0, 778, 117, 1, 0, 0, 0, 779, 780, 7, 0, 0, 0, 780, 781, 7, 3, 0, 0, 781, 782, 7, 2, 0, 0, 782, 783, 7, 4, 0, 0, 783, 119, 1, 0, 0, 0, 784, 785, 5, 46, 0, 0, 785, 121, 1, 0, 0, 0, 786, 787, 7, 15, 0, 0, 787, 788, 7, 12, 0, 0, 788, 789, 7, 13, 0, 0, 789, 790, 7, 2, 0, 0, 790, 791, 7, 3, 0, 0, 791, 123, 1, 0, 0, 0, 792, 793, 7, 15, 0, 0, 793, 794, 7, 1, 0, 0, 794, 795, 7, 6, 0, 0, 795, 796, 7, 2, 0, 0, 796, 797, 7, 5, 0, 0, 797, 125, 1, 0, 0, 0, 798, 799, 7, 13, 0, 0, 799, 800, 7, 12, 0, 0, 800, 801, 7, 2, 0, 0, 801, 802, 7, 5, 0, 0, 802, 127, 1, 0, 0, 0, 803, 804, 5, 40, 0, 0, 804, 129, 1, 0, 0, 0, 805, 806, 7, 1, 0, 0, 806, 807, 7, 9, 0, 0, 807, 131, 1, 0, 0, 0, 808, 809, 7, 1, 0, 0, 809, 810, 7, 2, 0, 0, 810, 133, 1, 0, 0, 0, 811, 812, 7, 13, 0, 0, 812, 813, 7, 1, 0, 0, 813, 814, 7, 18, 0, 0, 814, 815, 7, 3, 0, 0, 815, 135, 1, 0, 0, 0, 816, 817, 7, 9, 0, 0, 817, 818, 7, 7, 0, 0, 818, 819, 7, 5, 0, 0, 819, 137, 1, 0, 0, 0, 820, 821, 7, 9, 0, 0, 821, 822, 7, 19, 0, 0, 822, 823, 7, 13, 0, 0, 823, 824, 7, 13, 0, 0, 824, 139, 1, 0, 0, 0, 825, 826, 7, 9, 0, 0, 826, 827, 7, 19, 0, 0, 827, 828, 7, 13, 0, 0, 828, 829, 7, 13, 0, 0, 829, 830, 7, 2, 0, 0, 830, 141, 1, 0, 0, 0, 831, 832, 7, 7, 0, 0, 832, 833, 7, 6, 0, 0, 833, 143, 1, 0, 0, 0, 834, 835, 5, 63, 0, 0, 835, 145, 1, 0, 0, 0, 836, 837, 7, 6, 0, 0, 837, 838, 7, 13, 0, 0, 838, 839, 7, 1, 0, 0, 839, 840, 7, 18, 0, 0, 840, 841, 7, 3, 0, 0, 841, 147, 1, 0, 0, 0, 842, 843, 5, 41, 0, 0, 843, 149, 1, 0, 0, 0, 844, 845, 7, 5, 0, 0, 845, 846, 7, 6, 0, 0, 846, 847, 7, 19, 0, 0, 847, 848, 7, 3, 0, 0, 848, 151, 1, 0, 0, 0, 849, 850, 5, 61, 0, 0, 850, 851, 5, 61, 0, 0, 851, 153, 1, 0, 0, 0, 852, 853, 5, 61, 0, 0, 853, 854, 5, 126, 0, 0, 854, 155, 1, 0, 0, 0, 855, 856, 5, 33, 0, 0, 856, 857, 5, 61, 0, 0, 857, 157, 1, 0, 0, 0, 858, 859, 5, 60, 0, 0, 859, 159, 1, 0, 0, 0, 860, 861, 5, 60, 0, 0, 861, 862, 5, 61, 0, 0, 862, 161, 1, 0, 0, 0, 863, 864, 5, 62, 0, 0, 864, 163, 1, 0, 0, 0, 865, 866, 5, 62, 0, 0, 866, 867, 5, 61, 0, 0, 867, 165, 1, 0, 0, 0, 868, 869, 5, 43, 0, 0, 869, 167, 1, 0, 0, 0, 870, 871, 5, 45, 0, 0, 871, 169, 1, 0, 0, 0, 872, 873, 5, 42, 0, 0, 873, 171, 1, 0, 0, 0, 874, 875, 5, 47, 0, 0, 875, 173, 1, 0, 0, 0, 876, 877, 5, 37, 0, 0, 877, 175, 1, 0, 0, 0, 878, 879, 3, 144, 64, 0, 879, 883, 3, 82, 33, 0, 880, 882, 3, 98, 41, 0, 881, 880, 1, 0, 0, 0, 882, 885, 1, 0, 0, 0, 883, 881, 1, 0, 0, 0, 883, 884, 1, 0, 0, 0, 884, 893, 1, 0, 0, 0, 885, 883, 1, 0, 0, 0, 886, 888, 3, 144, 64, 0, 887, 889, 3, 80, 32, 0, 888, 887, 1, 0, 0, 0, 889, 890, 1, 0, 0, 0, 890, 888, 1, 0, 0, 0, 890, 891, 1, 0, 0, 0, 891, 893, 1, 0, 0, 0, 892, 878, 1, 0, 0, 0, 892, 886, 1, 0, 0, 0, 893, 177, 1, 0, 0, 0, 894, 895, 5, 91, 0, 0, 895, 896, 1, 0, 0, 0, 896, 897, 6, 81, 0, 0, 897, 898, 6, 81, 0, 0, 898, 179, 1, 0, 0, 0, 899, 900, 5, 93, 0, 0, 900, 901, 1, 0, 0, 0, 901, 902, 6, 82, 15, 0, 902, 903, 6, 82, 15, 0, 903, 181, 1, 0, 0, 0, 904, 908, 3, 82, 33, 0, 905, 907, 3, 98, 41, 0, 906, 905, 1, 0, 0, 0, 907, 910, 1, 0, 0, 0, 908, 906, 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 921, 1, 0, 0, 0, 910, 908, 1, 0, 0, 0, 911, 914, 3, 96, 40, 0, 912, 914, 3, 90, 37, 0, 913, 911, 1, 0, 0, 0, 913, 912, 1, 0, 0, 0, 914, 916, 1, 0, 0, 0, 915, 917, 3, 98, 41, 0, 916, 915, 1, 0, 0, 0, 917, 918, 1, 0, 0, 0, 918, 916, 1, 0, 0, 0, 918, 919, 1, 0, 0, 0, 919, 921, 1, 0, 0, 0, 920, 904, 1, 0, 0, 0, 920, 913, 1, 0, 0, 0, 921, 183, 1, 0, 0, 0, 922, 924, 3, 92, 38, 0, 923, 925, 3, 94, 39, 0, 924, 923, 1, 0, 0, 0, 925, 926, 1, 0, 0, 0, 926, 924, 1, 0, 0, 0, 926, 927, 1, 0, 0, 0, 927, 928, 1, 0, 0, 0, 928, 929, 3, 92, 38, 0, 929, 185, 1, 0, 0, 0, 930, 931, 3, 184, 84, 0, 931, 187, 1, 0, 0, 0, 932, 933, 3, 58, 21, 0, 933, 934, 1, 0, 0, 0, 934, 935, 6, 86, 11, 0, 935, 189, 1, 0, 0, 0, 936, 937, 3, 60, 22, 0, 937, 938, 1, 0, 0, 0, 938, 939, 6, 87, 11, 0, 939, 191, 1, 0, 0, 0, 940, 941, 3, 62, 23, 0, 941, 942, 1, 0, 0, 0, 942, 943, 6, 88, 11, 0, 943, 193, 1, 0, 0, 0, 944, 945, 3, 78, 31, 0, 945, 946, 1, 0, 0, 0, 946, 947, 6, 89, 14, 0, 947, 948, 6, 89, 15, 0, 948, 195, 1, 0, 0, 0, 949, 950, 3, 178, 81, 0, 950, 951, 1, 0, 0, 0, 951, 952, 6, 90, 12, 0, 952, 197, 1, 0, 0, 0, 953, 954, 3, 180, 82, 0, 954, 955, 1, 0, 0, 0, 955, 956, 6, 91, 16, 0, 956, 199, 1, 0, 0, 0, 957, 958, 3, 116, 50, 0, 958, 959, 1, 0, 0, 0, 959, 960, 6, 92, 17, 0, 960, 201, 1, 0, 0, 0, 961, 962, 3, 112, 48, 0, 962, 963, 1, 0, 0, 0, 963, 964, 6, 93, 18, 0, 964, 203, 1, 0, 0, 0, 965, 966, 3, 100, 42, 0, 966, 967, 1, 0, 0, 0, 967, 968, 6, 94, 19, 0, 968, 205, 1, 0, 0, 0, 969, 970, 7, 16, 0, 0, 970, 971, 7, 3, 0, 0, 971, 972, 7, 5, 0, 0, 972, 973, 7, 12, 0, 0, 973, 974, 7, 0, 0, 0, 974, 975, 7, 12, 0, 0, 975, 976, 7, 5, 0, 0, 976, 977, 7, 12, 0, 0, 977, 207, 1, 0, 0, 0, 978, 979, 3, 66, 25, 0, 979, 980, 1, 0, 0, 0, 980, 981, 6, 96, 20, 0, 981, 209, 1, 0, 0, 0, 982, 983, 3, 58, 21, 0, 983, 984, 1, 0, 0, 0, 984, 985, 6, 97, 11, 0, 985, 211, 1, 0, 0, 0, 986, 987, 3, 60, 22, 0, 987, 988, 1, 0, 0, 0, 988, 989, 6, 98, 11, 0, 989, 213, 1, 0, 0, 0, 990, 991, 3, 62, 23, 0, 991, 992, 1, 0, 0, 0, 992, 993, 6, 99, 11, 0, 993, 215, 1, 0, 0, 0, 994, 995, 3, 78, 31, 0, 995, 996, 1, 0, 0, 0, 996, 997, 6, 100, 14, 0, 997, 998, 6, 100, 15, 0, 998, 217, 1, 0, 0, 0, 999, 1000, 3, 120, 52, 0, 1000, 1001, 1, 0, 0, 0, 1001, 1002, 6, 101, 21, 0, 1002, 219, 1, 0, 0, 0, 1003, 1004, 3, 116, 50, 0, 1004, 1005, 1, 0, 0, 0, 1005, 1006, 6, 102, 17, 0, 1006, 221, 1, 0, 0, 0, 1007, 1012, 3, 82, 33, 0, 1008, 1012, 3, 80, 32, 0, 1009, 1012, 3, 96, 40, 0, 1010, 1012, 3, 170, 77, 0, 1011, 1007, 1, 0, 0, 0, 1011, 1008, 1, 0, 0, 0, 1011, 1009, 1, 0, 0, 0, 1011, 1010, 1, 0, 0, 0, 1012, 223, 1, 0, 0, 0, 1013, 1016, 3, 82, 33, 0, 1014, 1016, 3, 170, 77, 0, 1015, 1013, 1, 0, 0, 0, 1015, 1014, 1, 0, 0, 0, 1016, 1020, 1, 0, 0, 0, 1017, 1019, 3, 222, 103, 0, 1018, 1017, 1, 0, 0, 0, 1019, 1022, 1, 0, 0, 0, 1020, 1018, 1, 0, 0, 0, 1020, 1021, 1, 0, 0, 0, 1021, 1033, 1, 0, 0, 0, 1022, 1020, 1, 0, 0, 0, 1023, 1026, 3, 96, 40, 0, 1024, 1026, 3, 90, 37, 0, 1025, 1023, 1, 0, 0, 0, 1025, 1024, 1, 0, 0, 0, 1026, 1028, 1, 0, 0, 0, 1027, 1029, 3, 222, 103, 0, 1028, 1027, 1, 0, 0, 0, 1029, 1030, 1, 0, 0, 0, 1030, 1028, 1, 0, 0, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1033, 1, 0, 0, 0, 1032, 1015, 1, 0, 0, 0, 1032, 1025, 1, 0, 0, 0, 1033, 225, 1, 0, 0, 0, 1034, 1037, 3, 224, 104, 0, 1035, 1037, 3, 184, 84, 0, 1036, 1034, 1, 0, 0, 0, 1036, 1035, 1, 0, 0, 0, 1037, 1038, 1, 0, 0, 0, 1038, 1036, 1, 0, 0, 0, 1038, 1039, 1, 0, 0, 0, 1039, 227, 1, 0, 0, 0, 1040, 1041, 3, 58, 21, 0, 1041, 1042, 1, 0, 0, 0, 1042, 1043, 6, 106, 11, 0, 1043, 229, 1, 0, 0, 0, 1044, 1045, 3, 60, 22, 0, 1045, 1046, 1, 0, 0, 0, 1046, 1047, 6, 107, 11, 0, 1047, 231, 1, 0, 0, 0, 1048, 1049, 3, 62, 23, 0, 1049, 1050, 1, 0, 0, 0, 1050, 1051, 6, 108, 11, 0, 1051, 233, 1, 0, 0, 0, 1052, 1053, 3, 78, 31, 0, 1053, 1054, 1, 0, 0, 0, 1054, 1055, 6, 109, 14, 0, 1055, 1056, 6, 109, 15, 0, 1056, 235, 1, 0, 0, 0, 1057, 1058, 3, 112, 48, 0, 1058, 1059, 1, 0, 0, 0, 1059, 1060, 6, 110, 18, 0, 1060, 237, 1, 0, 0, 0, 1061, 1062, 3, 116, 50, 0, 1062, 1063, 1, 0, 0, 0, 1063, 1064, 6, 111, 17, 0, 1064, 239, 1, 0, 0, 0, 1065, 1066, 3, 120, 52, 0, 1066, 1067, 1, 0, 0, 0, 1067, 1068, 6, 112, 21, 0, 1068, 241, 1, 0, 0, 0, 1069, 1070, 7, 12, 0, 0, 1070, 1071, 7, 2, 0, 0, 1071, 243, 1, 0, 0, 0, 1072, 1073, 3, 226, 105, 0, 1073, 1074, 1, 0, 0, 0, 1074, 1075, 6, 114, 22, 0, 1075, 245, 1, 0, 0, 0, 1076, 1077, 3, 58, 21, 0, 1077, 1078, 1, 0, 0, 0, 1078, 1079, 6, 115, 11, 0, 1079, 247, 1, 0, 0, 0, 1080, 1081, 3, 60, 22, 0, 1081, 1082, 1, 0, 0, 0, 1082, 1083, 6, 116, 11, 0, 1083, 249, 1, 0, 0, 0, 1084, 1085, 3, 62, 23, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1087, 6, 117, 11, 0, 1087, 251, 1, 0, 0, 0, 1088, 1089, 3, 78, 31, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1091, 6, 118, 14, 0, 1091, 1092, 6, 118, 15, 0, 1092, 253, 1, 0, 0, 0, 1093, 1094, 3, 178, 81, 0, 1094, 1095, 1, 0, 0, 0, 1095, 1096, 6, 119, 12, 0, 1096, 1097, 6, 119, 23, 0, 1097, 255, 1, 0, 0, 0, 1098, 1099, 7, 7, 0, 0, 1099, 1100, 7, 9, 0, 0, 1100, 1101, 1, 0, 0, 0, 1101, 1102, 6, 120, 24, 0, 1102, 257, 1, 0, 0, 0, 1103, 1104, 7, 20, 0, 0, 1104, 1105, 7, 1, 0, 0, 1105, 1106, 7, 5, 0, 0, 1106, 1107, 7, 10, 0, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 121, 24, 0, 1109, 259, 1, 0, 0, 0, 1110, 1111, 8, 34, 0, 0, 1111, 261, 1, 0, 0, 0, 1112, 1114, 3, 260, 122, 0, 1113, 1112, 1, 0, 0, 0, 1114, 1115, 1, 0, 0, 0, 1115, 1113, 1, 0, 0, 0, 1115, 1116, 1, 0, 0, 0, 1116, 1117, 1, 0, 0, 0, 1117, 1118, 3, 360, 172, 0, 1118, 1120, 1, 0, 0, 0, 1119, 1113, 1, 0, 0, 0, 1119, 1120, 1, 0, 0, 0, 1120, 1122, 1, 0, 0, 0, 1121, 1123, 3, 260, 122, 0, 1122, 1121, 1, 0, 0, 0, 1123, 1124, 1, 0, 0, 0, 1124, 1122, 1, 0, 0, 0, 1124, 1125, 1, 0, 0, 0, 1125, 263, 1, 0, 0, 0, 1126, 1127, 3, 186, 85, 0, 1127, 1128, 1, 0, 0, 0, 1128, 1129, 6, 124, 25, 0, 1129, 265, 1, 0, 0, 0, 1130, 1131, 3, 262, 123, 0, 1131, 1132, 1, 0, 0, 0, 1132, 1133, 6, 125, 26, 0, 1133, 267, 1, 0, 0, 0, 1134, 1135, 3, 58, 21, 0, 1135, 1136, 1, 0, 0, 0, 1136, 1137, 6, 126, 11, 0, 1137, 269, 1, 0, 0, 0, 1138, 1139, 3, 60, 22, 0, 1139, 1140, 1, 0, 0, 0, 1140, 1141, 6, 127, 11, 0, 1141, 271, 1, 0, 0, 0, 1142, 1143, 3, 62, 23, 0, 1143, 1144, 1, 0, 0, 0, 1144, 1145, 6, 128, 11, 0, 1145, 273, 1, 0, 0, 0, 1146, 1147, 3, 78, 31, 0, 1147, 1148, 1, 0, 0, 0, 1148, 1149, 6, 129, 14, 0, 1149, 1150, 6, 129, 15, 0, 1150, 1151, 6, 129, 15, 0, 1151, 275, 1, 0, 0, 0, 1152, 1153, 3, 112, 48, 0, 1153, 1154, 1, 0, 0, 0, 1154, 1155, 6, 130, 18, 0, 1155, 277, 1, 0, 0, 0, 1156, 1157, 3, 116, 50, 0, 1157, 1158, 1, 0, 0, 0, 1158, 1159, 6, 131, 17, 0, 1159, 279, 1, 0, 0, 0, 1160, 1161, 3, 120, 52, 0, 1161, 1162, 1, 0, 0, 0, 1162, 1163, 6, 132, 21, 0, 1163, 281, 1, 0, 0, 0, 1164, 1165, 3, 258, 121, 0, 1165, 1166, 1, 0, 0, 0, 1166, 1167, 6, 133, 27, 0, 1167, 283, 1, 0, 0, 0, 1168, 1169, 3, 226, 105, 0, 1169, 1170, 1, 0, 0, 0, 1170, 1171, 6, 134, 22, 0, 1171, 285, 1, 0, 0, 0, 1172, 1173, 3, 186, 85, 0, 1173, 1174, 1, 0, 0, 0, 1174, 1175, 6, 135, 25, 0, 1175, 287, 1, 0, 0, 0, 1176, 1177, 3, 58, 21, 0, 1177, 1178, 1, 0, 0, 0, 1178, 1179, 6, 136, 11, 0, 1179, 289, 1, 0, 0, 0, 1180, 1181, 3, 60, 22, 0, 1181, 1182, 1, 0, 0, 0, 1182, 1183, 6, 137, 11, 0, 1183, 291, 1, 0, 0, 0, 1184, 1185, 3, 62, 23, 0, 1185, 1186, 1, 0, 0, 0, 1186, 1187, 6, 138, 11, 0, 1187, 293, 1, 0, 0, 0, 1188, 1189, 3, 78, 31, 0, 1189, 1190, 1, 0, 0, 0, 1190, 1191, 6, 139, 14, 0, 1191, 1192, 6, 139, 15, 0, 1192, 295, 1, 0, 0, 0, 1193, 1194, 3, 116, 50, 0, 1194, 1195, 1, 0, 0, 0, 1195, 1196, 6, 140, 17, 0, 1196, 297, 1, 0, 0, 0, 1197, 1198, 3, 120, 52, 0, 1198, 1199, 1, 0, 0, 0, 1199, 1200, 6, 141, 21, 0, 1200, 299, 1, 0, 0, 0, 1201, 1202, 3, 256, 120, 0, 1202, 1203, 1, 0, 0, 0, 1203, 1204, 6, 142, 28, 0, 1204, 1205, 6, 142, 29, 0, 1205, 301, 1, 0, 0, 0, 1206, 1207, 3, 66, 25, 0, 1207, 1208, 1, 0, 0, 0, 1208, 1209, 6, 143, 20, 0, 1209, 303, 1, 0, 0, 0, 1210, 1211, 3, 58, 21, 0, 1211, 1212, 1, 0, 0, 0, 1212, 1213, 6, 144, 11, 0, 1213, 305, 1, 0, 0, 0, 1214, 1215, 3, 60, 22, 0, 1215, 1216, 1, 0, 0, 0, 1216, 1217, 6, 145, 11, 0, 1217, 307, 1, 0, 0, 0, 1218, 1219, 3, 62, 23, 0, 1219, 1220, 1, 0, 0, 0, 1220, 1221, 6, 146, 11, 0, 1221, 309, 1, 0, 0, 0, 1222, 1223, 3, 78, 31, 0, 1223, 1224, 1, 0, 0, 0, 1224, 1225, 6, 147, 14, 0, 1225, 1226, 6, 147, 15, 0, 1226, 1227, 6, 147, 15, 0, 1227, 311, 1, 0, 0, 0, 1228, 1229, 3, 116, 50, 0, 1229, 1230, 1, 0, 0, 0, 1230, 1231, 6, 148, 17, 0, 1231, 313, 1, 0, 0, 0, 1232, 1233, 3, 120, 52, 0, 1233, 1234, 1, 0, 0, 0, 1234, 1235, 6, 149, 21, 0, 1235, 315, 1, 0, 0, 0, 1236, 1237, 3, 226, 105, 0, 1237, 1238, 1, 0, 0, 0, 1238, 1239, 6, 150, 22, 0, 1239, 317, 1, 0, 0, 0, 1240, 1241, 3, 58, 21, 0, 1241, 1242, 1, 0, 0, 0, 1242, 1243, 6, 151, 11, 0, 1243, 319, 1, 0, 0, 0, 1244, 1245, 3, 60, 22, 0, 1245, 1246, 1, 0, 0, 0, 1246, 1247, 6, 152, 11, 0, 1247, 321, 1, 0, 0, 0, 1248, 1249, 3, 62, 23, 0, 1249, 1250, 1, 0, 0, 0, 1250, 1251, 6, 153, 11, 0, 1251, 323, 1, 0, 0, 0, 1252, 1253, 3, 78, 31, 0, 1253, 1254, 1, 0, 0, 0, 1254, 1255, 6, 154, 14, 0, 1255, 1256, 6, 154, 15, 0, 1256, 325, 1, 0, 0, 0, 1257, 1258, 3, 120, 52, 0, 1258, 1259, 1, 0, 0, 0, 1259, 1260, 6, 155, 21, 0, 1260, 327, 1, 0, 0, 0, 1261, 1262, 3, 186, 85, 0, 1262, 1263, 1, 0, 0, 0, 1263, 1264, 6, 156, 25, 0, 1264, 329, 1, 0, 0, 0, 1265, 1266, 3, 182, 83, 0, 1266, 1267, 1, 0, 0, 0, 1267, 1268, 6, 157, 30, 0, 1268, 331, 1, 0, 0, 0, 1269, 1270, 3, 58, 21, 0, 1270, 1271, 1, 0, 0, 0, 1271, 1272, 6, 158, 11, 0, 1272, 333, 1, 0, 0, 0, 1273, 1274, 3, 60, 22, 0, 1274, 1275, 1, 0, 0, 0, 1275, 1276, 6, 159, 11, 0, 1276, 335, 1, 0, 0, 0, 1277, 1278, 3, 62, 23, 0, 1278, 1279, 1, 0, 0, 0, 1279, 1280, 6, 160, 11, 0, 1280, 337, 1, 0, 0, 0, 1281, 1282, 3, 78, 31, 0, 1282, 1283, 1, 0, 0, 0, 1283, 1284, 6, 161, 14, 0, 1284, 1285, 6, 161, 15, 0, 1285, 339, 1, 0, 0, 0, 1286, 1287, 7, 1, 0, 0, 1287, 1288, 7, 9, 0, 0, 1288, 1289, 7, 15, 0, 0, 1289, 1290, 7, 7, 0, 0, 1290, 341, 1, 0, 0, 0, 1291, 1292, 3, 58, 21, 0, 1292, 1293, 1, 0, 0, 0, 1293, 1294, 6, 163, 11, 0, 1294, 343, 1, 0, 0, 0, 1295, 1296, 3, 60, 22, 0, 1296, 1297, 1, 0, 0, 0, 1297, 1298, 6, 164, 11, 0, 1298, 345, 1, 0, 0, 0, 1299, 1300, 3, 62, 23, 0, 1300, 1301, 1, 0, 0, 0, 1301, 1302, 6, 165, 11, 0, 1302, 347, 1, 0, 0, 0, 1303, 1304, 3, 78, 31, 0, 1304, 1305, 1, 0, 0, 0, 1305, 1306, 6, 166, 14, 0, 1306, 1307, 6, 166, 15, 0, 1307, 349, 1, 0, 0, 0, 1308, 1309, 7, 15, 0, 0, 1309, 1310, 7, 19, 0, 0, 1310, 1311, 7, 9, 0, 0, 1311, 1312, 7, 4, 0, 0, 1312, 1313, 7, 5, 0, 0, 1313, 1314, 7, 1, 0, 0, 1314, 1315, 7, 7, 0, 0, 1315, 1316, 7, 9, 0, 0, 1316, 1317, 7, 2, 0, 0, 1317, 351, 1, 0, 0, 0, 1318, 1319, 3, 58, 21, 0, 1319, 1320, 1, 0, 0, 0, 1320, 1321, 6, 168, 11, 0, 1321, 353, 1, 0, 0, 0, 1322, 1323, 3, 60, 22, 0, 1323, 1324, 1, 0, 0, 0, 1324, 1325, 6, 169, 11, 0, 1325, 355, 1, 0, 0, 0, 1326, 1327, 3, 62, 23, 0, 1327, 1328, 1, 0, 0, 0, 1328, 1329, 6, 170, 11, 0, 1329, 357, 1, 0, 0, 0, 1330, 1331, 3, 180, 82, 0, 1331, 1332, 1, 0, 0, 0, 1332, 1333, 6, 171, 16, 0, 1333, 1334, 6, 171, 15, 0, 1334, 359, 1, 0, 0, 0, 1335, 1336, 5, 58, 0, 0, 1336, 361, 1, 0, 0, 0, 1337, 1343, 3, 90, 37, 0, 1338, 1343, 3, 80, 32, 0, 1339, 1343, 3, 120, 52, 0, 1340, 1343, 3, 82, 33, 0, 1341, 1343, 3, 96, 40, 0, 1342, 1337, 1, 0, 0, 0, 1342, 1338, 1, 0, 0, 0, 1342, 1339, 1, 0, 0, 0, 1342, 1340, 1, 0, 0, 0, 1342, 1341, 1, 0, 0, 0, 1343, 1344, 1, 0, 0, 0, 1344, 1342, 1, 0, 0, 0, 1344, 1345, 1, 0, 0, 0, 1345, 363, 1, 0, 0, 0, 1346, 1347, 3, 58, 21, 0, 1347, 1348, 1, 0, 0, 0, 1348, 1349, 6, 174, 11, 0, 1349, 365, 1, 0, 0, 0, 1350, 1351, 3, 60, 22, 0, 1351, 1352, 1, 0, 0, 0, 1352, 1353, 6, 175, 11, 0, 1353, 367, 1, 0, 0, 0, 1354, 1355, 3, 62, 23, 0, 1355, 1356, 1, 0, 0, 0, 1356, 1357, 6, 176, 11, 0, 1357, 369, 1, 0, 0, 0, 1358, 1359, 3, 78, 31, 0, 1359, 1360, 1, 0, 0, 0, 1360, 1361, 6, 177, 14, 0, 1361, 1362, 6, 177, 15, 0, 1362, 371, 1, 0, 0, 0, 1363, 1364, 3, 66, 25, 0, 1364, 1365, 1, 0, 0, 0, 1365, 1366, 6, 178, 20, 0, 1366, 1367, 6, 178, 15, 0, 1367, 1368, 6, 178, 31, 0, 1368, 373, 1, 0, 0, 0, 1369, 1370, 3, 58, 21, 0, 1370, 1371, 1, 0, 0, 0, 1371, 1372, 6, 179, 11, 0, 1372, 375, 1, 0, 0, 0, 1373, 1374, 3, 60, 22, 0, 1374, 1375, 1, 0, 0, 0, 1375, 1376, 6, 180, 11, 0, 1376, 377, 1, 0, 0, 0, 1377, 1378, 3, 62, 23, 0, 1378, 1379, 1, 0, 0, 0, 1379, 1380, 6, 181, 11, 0, 1380, 379, 1, 0, 0, 0, 1381, 1382, 3, 116, 50, 0, 1382, 1383, 1, 0, 0, 0, 1383, 1384, 6, 182, 17, 0, 1384, 1385, 6, 182, 15, 0, 1385, 1386, 6, 182, 7, 0, 1386, 381, 1, 0, 0, 0, 1387, 1388, 3, 58, 21, 0, 1388, 1389, 1, 0, 0, 0, 1389, 1390, 6, 183, 11, 0, 1390, 383, 1, 0, 0, 0, 1391, 1392, 3, 60, 22, 0, 1392, 1393, 1, 0, 0, 0, 1393, 1394, 6, 184, 11, 0, 1394, 385, 1, 0, 0, 0, 1395, 1396, 3, 62, 23, 0, 1396, 1397, 1, 0, 0, 0, 1397, 1398, 6, 185, 11, 0, 1398, 387, 1, 0, 0, 0, 1399, 1400, 3, 186, 85, 0, 1400, 1401, 1, 0, 0, 0, 1401, 1402, 6, 186, 15, 0, 1402, 1403, 6, 186, 0, 0, 1403, 1404, 6, 186, 25, 0, 1404, 389, 1, 0, 0, 0, 1405, 1406, 3, 182, 83, 0, 1406, 1407, 1, 0, 0, 0, 1407, 1408, 6, 187, 15, 0, 1408, 1409, 6, 187, 0, 0, 1409, 1410, 6, 187, 30, 0, 1410, 391, 1, 0, 0, 0, 1411, 1412, 3, 106, 45, 0, 1412, 1413, 1, 0, 0, 0, 1413, 1414, 6, 188, 15, 0, 1414, 1415, 6, 188, 0, 0, 1415, 1416, 6, 188, 32, 0, 1416, 393, 1, 0, 0, 0, 1417, 1418, 3, 78, 31, 0, 1418, 1419, 1, 0, 0, 0, 1419, 1420, 6, 189, 14, 0, 1420, 1421, 6, 189, 15, 0, 1421, 395, 1, 0, 0, 0, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 568, 578, 582, 585, 594, 596, 607, 614, 619, 658, 663, 672, 679, 684, 686, 697, 705, 708, 710, 715, 720, 726, 733, 738, 744, 747, 755, 759, 883, 890, 892, 908, 913, 918, 920, 926, 1011, 1015, 1020, 1025, 1030, 1032, 1036, 1038, 1115, 1119, 1124, 1342, 1344, 33, 5, 2, 0, 5, 4, 0, 5, 6, 0, 5, 1, 0, 5, 3, 0, 5, 8, 0, 5, 12, 0, 5, 14, 0, 5, 10, 0, 5, 5, 0, 5, 11, 0, 0, 1, 0, 7, 69, 0, 5, 0, 0, 7, 29, 0, 4, 0, 0, 7, 70, 0, 7, 38, 0, 7, 36, 0, 7, 30, 0, 7, 25, 0, 7, 40, 0, 7, 80, 0, 5, 13, 0, 5, 7, 0, 7, 72, 0, 7, 90, 0, 7, 89, 0, 7, 88, 0, 5, 9, 0, 7, 71, 0, 5, 15, 0, 7, 33, 0] \ No newline at end of file +[4, 0, 124, 1450, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 4, 20, 575, 8, 20, 11, 20, 12, 20, 576, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 585, 8, 21, 10, 21, 12, 21, 588, 9, 21, 1, 21, 3, 21, 591, 8, 21, 1, 21, 3, 21, 594, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 5, 22, 603, 8, 22, 10, 22, 12, 22, 606, 9, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 4, 23, 614, 8, 23, 11, 23, 12, 23, 615, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 3, 24, 623, 8, 24, 1, 25, 4, 25, 626, 8, 25, 11, 25, 12, 25, 627, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 3, 36, 667, 8, 36, 1, 36, 4, 36, 670, 8, 36, 11, 36, 12, 36, 671, 1, 37, 1, 37, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 3, 39, 681, 8, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 3, 41, 688, 8, 41, 1, 42, 1, 42, 1, 42, 5, 42, 693, 8, 42, 10, 42, 12, 42, 696, 9, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 704, 8, 42, 10, 42, 12, 42, 707, 9, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 714, 8, 42, 1, 42, 3, 42, 717, 8, 42, 3, 42, 719, 8, 42, 1, 43, 4, 43, 722, 8, 43, 11, 43, 12, 43, 723, 1, 44, 4, 44, 727, 8, 44, 11, 44, 12, 44, 728, 1, 44, 1, 44, 5, 44, 733, 8, 44, 10, 44, 12, 44, 736, 9, 44, 1, 44, 1, 44, 4, 44, 740, 8, 44, 11, 44, 12, 44, 741, 1, 44, 4, 44, 745, 8, 44, 11, 44, 12, 44, 746, 1, 44, 1, 44, 5, 44, 751, 8, 44, 10, 44, 12, 44, 754, 9, 44, 3, 44, 756, 8, 44, 1, 44, 1, 44, 1, 44, 1, 44, 4, 44, 762, 8, 44, 11, 44, 12, 44, 763, 1, 44, 1, 44, 3, 44, 768, 8, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 5, 80, 890, 8, 80, 10, 80, 12, 80, 893, 9, 80, 1, 80, 1, 80, 4, 80, 897, 8, 80, 11, 80, 12, 80, 898, 3, 80, 901, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 5, 83, 915, 8, 83, 10, 83, 12, 83, 918, 9, 83, 1, 83, 1, 83, 3, 83, 922, 8, 83, 1, 83, 4, 83, 925, 8, 83, 11, 83, 12, 83, 926, 3, 83, 929, 8, 83, 1, 84, 1, 84, 4, 84, 933, 8, 84, 11, 84, 12, 84, 934, 1, 84, 1, 84, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 3, 104, 1024, 8, 104, 1, 105, 1, 105, 3, 105, 1028, 8, 105, 1, 105, 5, 105, 1031, 8, 105, 10, 105, 12, 105, 1034, 9, 105, 1, 105, 1, 105, 3, 105, 1038, 8, 105, 1, 105, 4, 105, 1041, 8, 105, 11, 105, 12, 105, 1042, 3, 105, 1045, 8, 105, 1, 106, 1, 106, 4, 106, 1049, 8, 106, 11, 106, 12, 106, 1050, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 124, 4, 124, 1126, 8, 124, 11, 124, 12, 124, 1127, 1, 124, 1, 124, 3, 124, 1132, 8, 124, 1, 124, 4, 124, 1135, 8, 124, 11, 124, 12, 124, 1136, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 175, 4, 175, 1359, 8, 175, 11, 175, 12, 175, 1360, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 2, 604, 705, 0, 194, 16, 1, 18, 2, 20, 3, 22, 4, 24, 5, 26, 6, 28, 7, 30, 8, 32, 9, 34, 10, 36, 11, 38, 12, 40, 13, 42, 14, 44, 15, 46, 16, 48, 17, 50, 18, 52, 19, 54, 20, 56, 21, 58, 22, 60, 23, 62, 24, 64, 0, 66, 25, 68, 0, 70, 0, 72, 26, 74, 27, 76, 28, 78, 29, 80, 0, 82, 0, 84, 0, 86, 0, 88, 0, 90, 0, 92, 0, 94, 0, 96, 0, 98, 0, 100, 30, 102, 31, 104, 32, 106, 33, 108, 34, 110, 35, 112, 36, 114, 37, 116, 38, 118, 39, 120, 40, 122, 41, 124, 42, 126, 43, 128, 44, 130, 45, 132, 46, 134, 47, 136, 48, 138, 49, 140, 50, 142, 51, 144, 52, 146, 53, 148, 54, 150, 55, 152, 56, 154, 57, 156, 58, 158, 59, 160, 60, 162, 61, 164, 62, 166, 63, 168, 64, 170, 65, 172, 66, 174, 67, 176, 68, 178, 69, 180, 70, 182, 71, 184, 0, 186, 72, 188, 73, 190, 74, 192, 75, 194, 0, 196, 0, 198, 0, 200, 0, 202, 0, 204, 0, 206, 76, 208, 0, 210, 0, 212, 77, 214, 78, 216, 79, 218, 0, 220, 0, 222, 0, 224, 0, 226, 0, 228, 80, 230, 81, 232, 82, 234, 83, 236, 0, 238, 0, 240, 0, 242, 0, 244, 84, 246, 0, 248, 85, 250, 86, 252, 87, 254, 0, 256, 0, 258, 88, 260, 89, 262, 0, 264, 90, 266, 0, 268, 91, 270, 92, 272, 93, 274, 0, 276, 0, 278, 0, 280, 0, 282, 0, 284, 0, 286, 0, 288, 94, 290, 95, 292, 96, 294, 0, 296, 0, 298, 0, 300, 0, 302, 0, 304, 0, 306, 0, 308, 97, 310, 98, 312, 99, 314, 0, 316, 0, 318, 0, 320, 0, 322, 100, 324, 101, 326, 102, 328, 0, 330, 0, 332, 0, 334, 0, 336, 103, 338, 104, 340, 105, 342, 0, 344, 106, 346, 107, 348, 108, 350, 109, 352, 0, 354, 110, 356, 111, 358, 112, 360, 113, 362, 0, 364, 114, 366, 115, 368, 116, 370, 117, 372, 118, 374, 0, 376, 0, 378, 0, 380, 119, 382, 120, 384, 121, 386, 0, 388, 0, 390, 122, 392, 123, 394, 124, 396, 0, 398, 0, 400, 0, 402, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 85, 85, 117, 117, 2, 0, 87, 87, 119, 119, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1476, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 0, 56, 1, 0, 0, 0, 0, 58, 1, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 62, 1, 0, 0, 0, 0, 66, 1, 0, 0, 0, 1, 68, 1, 0, 0, 0, 1, 70, 1, 0, 0, 0, 1, 72, 1, 0, 0, 0, 1, 74, 1, 0, 0, 0, 1, 76, 1, 0, 0, 0, 2, 78, 1, 0, 0, 0, 2, 100, 1, 0, 0, 0, 2, 102, 1, 0, 0, 0, 2, 104, 1, 0, 0, 0, 2, 106, 1, 0, 0, 0, 2, 108, 1, 0, 0, 0, 2, 110, 1, 0, 0, 0, 2, 112, 1, 0, 0, 0, 2, 114, 1, 0, 0, 0, 2, 116, 1, 0, 0, 0, 2, 118, 1, 0, 0, 0, 2, 120, 1, 0, 0, 0, 2, 122, 1, 0, 0, 0, 2, 124, 1, 0, 0, 0, 2, 126, 1, 0, 0, 0, 2, 128, 1, 0, 0, 0, 2, 130, 1, 0, 0, 0, 2, 132, 1, 0, 0, 0, 2, 134, 1, 0, 0, 0, 2, 136, 1, 0, 0, 0, 2, 138, 1, 0, 0, 0, 2, 140, 1, 0, 0, 0, 2, 142, 1, 0, 0, 0, 2, 144, 1, 0, 0, 0, 2, 146, 1, 0, 0, 0, 2, 148, 1, 0, 0, 0, 2, 150, 1, 0, 0, 0, 2, 152, 1, 0, 0, 0, 2, 154, 1, 0, 0, 0, 2, 156, 1, 0, 0, 0, 2, 158, 1, 0, 0, 0, 2, 160, 1, 0, 0, 0, 2, 162, 1, 0, 0, 0, 2, 164, 1, 0, 0, 0, 2, 166, 1, 0, 0, 0, 2, 168, 1, 0, 0, 0, 2, 170, 1, 0, 0, 0, 2, 172, 1, 0, 0, 0, 2, 174, 1, 0, 0, 0, 2, 176, 1, 0, 0, 0, 2, 178, 1, 0, 0, 0, 2, 180, 1, 0, 0, 0, 2, 182, 1, 0, 0, 0, 2, 186, 1, 0, 0, 0, 2, 188, 1, 0, 0, 0, 2, 190, 1, 0, 0, 0, 2, 192, 1, 0, 0, 0, 3, 194, 1, 0, 0, 0, 3, 196, 1, 0, 0, 0, 3, 198, 1, 0, 0, 0, 3, 200, 1, 0, 0, 0, 3, 202, 1, 0, 0, 0, 3, 204, 1, 0, 0, 0, 3, 206, 1, 0, 0, 0, 3, 208, 1, 0, 0, 0, 3, 210, 1, 0, 0, 0, 3, 212, 1, 0, 0, 0, 3, 214, 1, 0, 0, 0, 3, 216, 1, 0, 0, 0, 4, 218, 1, 0, 0, 0, 4, 220, 1, 0, 0, 0, 4, 222, 1, 0, 0, 0, 4, 228, 1, 0, 0, 0, 4, 230, 1, 0, 0, 0, 4, 232, 1, 0, 0, 0, 4, 234, 1, 0, 0, 0, 5, 236, 1, 0, 0, 0, 5, 238, 1, 0, 0, 0, 5, 240, 1, 0, 0, 0, 5, 242, 1, 0, 0, 0, 5, 244, 1, 0, 0, 0, 5, 246, 1, 0, 0, 0, 5, 248, 1, 0, 0, 0, 5, 250, 1, 0, 0, 0, 5, 252, 1, 0, 0, 0, 6, 254, 1, 0, 0, 0, 6, 256, 1, 0, 0, 0, 6, 258, 1, 0, 0, 0, 6, 260, 1, 0, 0, 0, 6, 264, 1, 0, 0, 0, 6, 266, 1, 0, 0, 0, 6, 268, 1, 0, 0, 0, 6, 270, 1, 0, 0, 0, 6, 272, 1, 0, 0, 0, 7, 274, 1, 0, 0, 0, 7, 276, 1, 0, 0, 0, 7, 278, 1, 0, 0, 0, 7, 280, 1, 0, 0, 0, 7, 282, 1, 0, 0, 0, 7, 284, 1, 0, 0, 0, 7, 286, 1, 0, 0, 0, 7, 288, 1, 0, 0, 0, 7, 290, 1, 0, 0, 0, 7, 292, 1, 0, 0, 0, 8, 294, 1, 0, 0, 0, 8, 296, 1, 0, 0, 0, 8, 298, 1, 0, 0, 0, 8, 300, 1, 0, 0, 0, 8, 302, 1, 0, 0, 0, 8, 304, 1, 0, 0, 0, 8, 306, 1, 0, 0, 0, 8, 308, 1, 0, 0, 0, 8, 310, 1, 0, 0, 0, 8, 312, 1, 0, 0, 0, 9, 314, 1, 0, 0, 0, 9, 316, 1, 0, 0, 0, 9, 318, 1, 0, 0, 0, 9, 320, 1, 0, 0, 0, 9, 322, 1, 0, 0, 0, 9, 324, 1, 0, 0, 0, 9, 326, 1, 0, 0, 0, 10, 328, 1, 0, 0, 0, 10, 330, 1, 0, 0, 0, 10, 332, 1, 0, 0, 0, 10, 334, 1, 0, 0, 0, 10, 336, 1, 0, 0, 0, 10, 338, 1, 0, 0, 0, 10, 340, 1, 0, 0, 0, 11, 342, 1, 0, 0, 0, 11, 344, 1, 0, 0, 0, 11, 346, 1, 0, 0, 0, 11, 348, 1, 0, 0, 0, 11, 350, 1, 0, 0, 0, 12, 352, 1, 0, 0, 0, 12, 354, 1, 0, 0, 0, 12, 356, 1, 0, 0, 0, 12, 358, 1, 0, 0, 0, 12, 360, 1, 0, 0, 0, 13, 362, 1, 0, 0, 0, 13, 364, 1, 0, 0, 0, 13, 366, 1, 0, 0, 0, 13, 368, 1, 0, 0, 0, 13, 370, 1, 0, 0, 0, 13, 372, 1, 0, 0, 0, 14, 374, 1, 0, 0, 0, 14, 376, 1, 0, 0, 0, 14, 378, 1, 0, 0, 0, 14, 380, 1, 0, 0, 0, 14, 382, 1, 0, 0, 0, 14, 384, 1, 0, 0, 0, 15, 386, 1, 0, 0, 0, 15, 388, 1, 0, 0, 0, 15, 390, 1, 0, 0, 0, 15, 392, 1, 0, 0, 0, 15, 394, 1, 0, 0, 0, 15, 396, 1, 0, 0, 0, 15, 398, 1, 0, 0, 0, 15, 400, 1, 0, 0, 0, 15, 402, 1, 0, 0, 0, 16, 404, 1, 0, 0, 0, 18, 414, 1, 0, 0, 0, 20, 421, 1, 0, 0, 0, 22, 430, 1, 0, 0, 0, 24, 437, 1, 0, 0, 0, 26, 447, 1, 0, 0, 0, 28, 454, 1, 0, 0, 0, 30, 461, 1, 0, 0, 0, 32, 475, 1, 0, 0, 0, 34, 482, 1, 0, 0, 0, 36, 490, 1, 0, 0, 0, 38, 499, 1, 0, 0, 0, 40, 506, 1, 0, 0, 0, 42, 516, 1, 0, 0, 0, 44, 528, 1, 0, 0, 0, 46, 537, 1, 0, 0, 0, 48, 543, 1, 0, 0, 0, 50, 550, 1, 0, 0, 0, 52, 557, 1, 0, 0, 0, 54, 565, 1, 0, 0, 0, 56, 574, 1, 0, 0, 0, 58, 580, 1, 0, 0, 0, 60, 597, 1, 0, 0, 0, 62, 613, 1, 0, 0, 0, 64, 622, 1, 0, 0, 0, 66, 625, 1, 0, 0, 0, 68, 629, 1, 0, 0, 0, 70, 634, 1, 0, 0, 0, 72, 639, 1, 0, 0, 0, 74, 643, 1, 0, 0, 0, 76, 647, 1, 0, 0, 0, 78, 651, 1, 0, 0, 0, 80, 655, 1, 0, 0, 0, 82, 657, 1, 0, 0, 0, 84, 659, 1, 0, 0, 0, 86, 662, 1, 0, 0, 0, 88, 664, 1, 0, 0, 0, 90, 673, 1, 0, 0, 0, 92, 675, 1, 0, 0, 0, 94, 680, 1, 0, 0, 0, 96, 682, 1, 0, 0, 0, 98, 687, 1, 0, 0, 0, 100, 718, 1, 0, 0, 0, 102, 721, 1, 0, 0, 0, 104, 767, 1, 0, 0, 0, 106, 769, 1, 0, 0, 0, 108, 772, 1, 0, 0, 0, 110, 776, 1, 0, 0, 0, 112, 780, 1, 0, 0, 0, 114, 782, 1, 0, 0, 0, 116, 785, 1, 0, 0, 0, 118, 787, 1, 0, 0, 0, 120, 792, 1, 0, 0, 0, 122, 794, 1, 0, 0, 0, 124, 800, 1, 0, 0, 0, 126, 806, 1, 0, 0, 0, 128, 811, 1, 0, 0, 0, 130, 813, 1, 0, 0, 0, 132, 816, 1, 0, 0, 0, 134, 819, 1, 0, 0, 0, 136, 824, 1, 0, 0, 0, 138, 828, 1, 0, 0, 0, 140, 833, 1, 0, 0, 0, 142, 839, 1, 0, 0, 0, 144, 842, 1, 0, 0, 0, 146, 844, 1, 0, 0, 0, 148, 850, 1, 0, 0, 0, 150, 852, 1, 0, 0, 0, 152, 857, 1, 0, 0, 0, 154, 860, 1, 0, 0, 0, 156, 863, 1, 0, 0, 0, 158, 866, 1, 0, 0, 0, 160, 868, 1, 0, 0, 0, 162, 871, 1, 0, 0, 0, 164, 873, 1, 0, 0, 0, 166, 876, 1, 0, 0, 0, 168, 878, 1, 0, 0, 0, 170, 880, 1, 0, 0, 0, 172, 882, 1, 0, 0, 0, 174, 884, 1, 0, 0, 0, 176, 900, 1, 0, 0, 0, 178, 902, 1, 0, 0, 0, 180, 907, 1, 0, 0, 0, 182, 928, 1, 0, 0, 0, 184, 930, 1, 0, 0, 0, 186, 938, 1, 0, 0, 0, 188, 940, 1, 0, 0, 0, 190, 944, 1, 0, 0, 0, 192, 948, 1, 0, 0, 0, 194, 952, 1, 0, 0, 0, 196, 957, 1, 0, 0, 0, 198, 961, 1, 0, 0, 0, 200, 965, 1, 0, 0, 0, 202, 969, 1, 0, 0, 0, 204, 973, 1, 0, 0, 0, 206, 977, 1, 0, 0, 0, 208, 986, 1, 0, 0, 0, 210, 990, 1, 0, 0, 0, 212, 994, 1, 0, 0, 0, 214, 998, 1, 0, 0, 0, 216, 1002, 1, 0, 0, 0, 218, 1006, 1, 0, 0, 0, 220, 1011, 1, 0, 0, 0, 222, 1015, 1, 0, 0, 0, 224, 1023, 1, 0, 0, 0, 226, 1044, 1, 0, 0, 0, 228, 1048, 1, 0, 0, 0, 230, 1052, 1, 0, 0, 0, 232, 1056, 1, 0, 0, 0, 234, 1060, 1, 0, 0, 0, 236, 1064, 1, 0, 0, 0, 238, 1069, 1, 0, 0, 0, 240, 1073, 1, 0, 0, 0, 242, 1077, 1, 0, 0, 0, 244, 1081, 1, 0, 0, 0, 246, 1084, 1, 0, 0, 0, 248, 1088, 1, 0, 0, 0, 250, 1092, 1, 0, 0, 0, 252, 1096, 1, 0, 0, 0, 254, 1100, 1, 0, 0, 0, 256, 1105, 1, 0, 0, 0, 258, 1110, 1, 0, 0, 0, 260, 1115, 1, 0, 0, 0, 262, 1122, 1, 0, 0, 0, 264, 1131, 1, 0, 0, 0, 266, 1138, 1, 0, 0, 0, 268, 1142, 1, 0, 0, 0, 270, 1146, 1, 0, 0, 0, 272, 1150, 1, 0, 0, 0, 274, 1154, 1, 0, 0, 0, 276, 1160, 1, 0, 0, 0, 278, 1164, 1, 0, 0, 0, 280, 1168, 1, 0, 0, 0, 282, 1172, 1, 0, 0, 0, 284, 1176, 1, 0, 0, 0, 286, 1180, 1, 0, 0, 0, 288, 1184, 1, 0, 0, 0, 290, 1188, 1, 0, 0, 0, 292, 1192, 1, 0, 0, 0, 294, 1196, 1, 0, 0, 0, 296, 1201, 1, 0, 0, 0, 298, 1205, 1, 0, 0, 0, 300, 1209, 1, 0, 0, 0, 302, 1213, 1, 0, 0, 0, 304, 1218, 1, 0, 0, 0, 306, 1222, 1, 0, 0, 0, 308, 1226, 1, 0, 0, 0, 310, 1230, 1, 0, 0, 0, 312, 1234, 1, 0, 0, 0, 314, 1238, 1, 0, 0, 0, 316, 1244, 1, 0, 0, 0, 318, 1248, 1, 0, 0, 0, 320, 1252, 1, 0, 0, 0, 322, 1256, 1, 0, 0, 0, 324, 1260, 1, 0, 0, 0, 326, 1264, 1, 0, 0, 0, 328, 1268, 1, 0, 0, 0, 330, 1273, 1, 0, 0, 0, 332, 1277, 1, 0, 0, 0, 334, 1281, 1, 0, 0, 0, 336, 1285, 1, 0, 0, 0, 338, 1289, 1, 0, 0, 0, 340, 1293, 1, 0, 0, 0, 342, 1297, 1, 0, 0, 0, 344, 1302, 1, 0, 0, 0, 346, 1307, 1, 0, 0, 0, 348, 1311, 1, 0, 0, 0, 350, 1315, 1, 0, 0, 0, 352, 1319, 1, 0, 0, 0, 354, 1324, 1, 0, 0, 0, 356, 1334, 1, 0, 0, 0, 358, 1338, 1, 0, 0, 0, 360, 1342, 1, 0, 0, 0, 362, 1346, 1, 0, 0, 0, 364, 1351, 1, 0, 0, 0, 366, 1358, 1, 0, 0, 0, 368, 1362, 1, 0, 0, 0, 370, 1366, 1, 0, 0, 0, 372, 1370, 1, 0, 0, 0, 374, 1374, 1, 0, 0, 0, 376, 1379, 1, 0, 0, 0, 378, 1385, 1, 0, 0, 0, 380, 1391, 1, 0, 0, 0, 382, 1395, 1, 0, 0, 0, 384, 1399, 1, 0, 0, 0, 386, 1403, 1, 0, 0, 0, 388, 1409, 1, 0, 0, 0, 390, 1415, 1, 0, 0, 0, 392, 1419, 1, 0, 0, 0, 394, 1423, 1, 0, 0, 0, 396, 1427, 1, 0, 0, 0, 398, 1433, 1, 0, 0, 0, 400, 1439, 1, 0, 0, 0, 402, 1445, 1, 0, 0, 0, 404, 405, 7, 0, 0, 0, 405, 406, 7, 1, 0, 0, 406, 407, 7, 2, 0, 0, 407, 408, 7, 2, 0, 0, 408, 409, 7, 3, 0, 0, 409, 410, 7, 4, 0, 0, 410, 411, 7, 5, 0, 0, 411, 412, 1, 0, 0, 0, 412, 413, 6, 0, 0, 0, 413, 17, 1, 0, 0, 0, 414, 415, 7, 0, 0, 0, 415, 416, 7, 6, 0, 0, 416, 417, 7, 7, 0, 0, 417, 418, 7, 8, 0, 0, 418, 419, 1, 0, 0, 0, 419, 420, 6, 1, 1, 0, 420, 19, 1, 0, 0, 0, 421, 422, 7, 3, 0, 0, 422, 423, 7, 9, 0, 0, 423, 424, 7, 6, 0, 0, 424, 425, 7, 1, 0, 0, 425, 426, 7, 4, 0, 0, 426, 427, 7, 10, 0, 0, 427, 428, 1, 0, 0, 0, 428, 429, 6, 2, 2, 0, 429, 21, 1, 0, 0, 0, 430, 431, 7, 3, 0, 0, 431, 432, 7, 11, 0, 0, 432, 433, 7, 12, 0, 0, 433, 434, 7, 13, 0, 0, 434, 435, 1, 0, 0, 0, 435, 436, 6, 3, 0, 0, 436, 23, 1, 0, 0, 0, 437, 438, 7, 3, 0, 0, 438, 439, 7, 14, 0, 0, 439, 440, 7, 8, 0, 0, 440, 441, 7, 13, 0, 0, 441, 442, 7, 12, 0, 0, 442, 443, 7, 1, 0, 0, 443, 444, 7, 9, 0, 0, 444, 445, 1, 0, 0, 0, 445, 446, 6, 4, 3, 0, 446, 25, 1, 0, 0, 0, 447, 448, 7, 15, 0, 0, 448, 449, 7, 6, 0, 0, 449, 450, 7, 7, 0, 0, 450, 451, 7, 16, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 6, 5, 4, 0, 453, 27, 1, 0, 0, 0, 454, 455, 7, 17, 0, 0, 455, 456, 7, 6, 0, 0, 456, 457, 7, 7, 0, 0, 457, 458, 7, 18, 0, 0, 458, 459, 1, 0, 0, 0, 459, 460, 6, 6, 0, 0, 460, 29, 1, 0, 0, 0, 461, 462, 7, 1, 0, 0, 462, 463, 7, 9, 0, 0, 463, 464, 7, 13, 0, 0, 464, 465, 7, 1, 0, 0, 465, 466, 7, 9, 0, 0, 466, 467, 7, 3, 0, 0, 467, 468, 7, 2, 0, 0, 468, 469, 7, 5, 0, 0, 469, 470, 7, 12, 0, 0, 470, 471, 7, 5, 0, 0, 471, 472, 7, 2, 0, 0, 472, 473, 1, 0, 0, 0, 473, 474, 6, 7, 0, 0, 474, 31, 1, 0, 0, 0, 475, 476, 7, 18, 0, 0, 476, 477, 7, 3, 0, 0, 477, 478, 7, 3, 0, 0, 478, 479, 7, 8, 0, 0, 479, 480, 1, 0, 0, 0, 480, 481, 6, 8, 1, 0, 481, 33, 1, 0, 0, 0, 482, 483, 7, 13, 0, 0, 483, 484, 7, 1, 0, 0, 484, 485, 7, 16, 0, 0, 485, 486, 7, 1, 0, 0, 486, 487, 7, 5, 0, 0, 487, 488, 1, 0, 0, 0, 488, 489, 6, 9, 0, 0, 489, 35, 1, 0, 0, 0, 490, 491, 7, 13, 0, 0, 491, 492, 7, 7, 0, 0, 492, 493, 7, 7, 0, 0, 493, 494, 7, 18, 0, 0, 494, 495, 7, 19, 0, 0, 495, 496, 7, 8, 0, 0, 496, 497, 1, 0, 0, 0, 497, 498, 6, 10, 5, 0, 498, 37, 1, 0, 0, 0, 499, 500, 7, 16, 0, 0, 500, 501, 7, 3, 0, 0, 501, 502, 7, 5, 0, 0, 502, 503, 7, 12, 0, 0, 503, 504, 1, 0, 0, 0, 504, 505, 6, 11, 6, 0, 505, 39, 1, 0, 0, 0, 506, 507, 7, 16, 0, 0, 507, 508, 7, 3, 0, 0, 508, 509, 7, 5, 0, 0, 509, 510, 7, 6, 0, 0, 510, 511, 7, 1, 0, 0, 511, 512, 7, 4, 0, 0, 512, 513, 7, 2, 0, 0, 513, 514, 1, 0, 0, 0, 514, 515, 6, 12, 7, 0, 515, 41, 1, 0, 0, 0, 516, 517, 7, 16, 0, 0, 517, 518, 7, 11, 0, 0, 518, 519, 5, 95, 0, 0, 519, 520, 7, 3, 0, 0, 520, 521, 7, 14, 0, 0, 521, 522, 7, 8, 0, 0, 522, 523, 7, 12, 0, 0, 523, 524, 7, 9, 0, 0, 524, 525, 7, 0, 0, 0, 525, 526, 1, 0, 0, 0, 526, 527, 6, 13, 8, 0, 527, 43, 1, 0, 0, 0, 528, 529, 7, 6, 0, 0, 529, 530, 7, 3, 0, 0, 530, 531, 7, 9, 0, 0, 531, 532, 7, 12, 0, 0, 532, 533, 7, 16, 0, 0, 533, 534, 7, 3, 0, 0, 534, 535, 1, 0, 0, 0, 535, 536, 6, 14, 9, 0, 536, 45, 1, 0, 0, 0, 537, 538, 7, 6, 0, 0, 538, 539, 7, 7, 0, 0, 539, 540, 7, 20, 0, 0, 540, 541, 1, 0, 0, 0, 541, 542, 6, 15, 0, 0, 542, 47, 1, 0, 0, 0, 543, 544, 7, 2, 0, 0, 544, 545, 7, 10, 0, 0, 545, 546, 7, 7, 0, 0, 546, 547, 7, 20, 0, 0, 547, 548, 1, 0, 0, 0, 548, 549, 6, 16, 10, 0, 549, 49, 1, 0, 0, 0, 550, 551, 7, 2, 0, 0, 551, 552, 7, 7, 0, 0, 552, 553, 7, 6, 0, 0, 553, 554, 7, 5, 0, 0, 554, 555, 1, 0, 0, 0, 555, 556, 6, 17, 0, 0, 556, 51, 1, 0, 0, 0, 557, 558, 7, 2, 0, 0, 558, 559, 7, 5, 0, 0, 559, 560, 7, 12, 0, 0, 560, 561, 7, 5, 0, 0, 561, 562, 7, 2, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 6, 18, 0, 0, 564, 53, 1, 0, 0, 0, 565, 566, 7, 20, 0, 0, 566, 567, 7, 10, 0, 0, 567, 568, 7, 3, 0, 0, 568, 569, 7, 6, 0, 0, 569, 570, 7, 3, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 6, 19, 0, 0, 572, 55, 1, 0, 0, 0, 573, 575, 8, 21, 0, 0, 574, 573, 1, 0, 0, 0, 575, 576, 1, 0, 0, 0, 576, 574, 1, 0, 0, 0, 576, 577, 1, 0, 0, 0, 577, 578, 1, 0, 0, 0, 578, 579, 6, 20, 0, 0, 579, 57, 1, 0, 0, 0, 580, 581, 5, 47, 0, 0, 581, 582, 5, 47, 0, 0, 582, 586, 1, 0, 0, 0, 583, 585, 8, 22, 0, 0, 584, 583, 1, 0, 0, 0, 585, 588, 1, 0, 0, 0, 586, 584, 1, 0, 0, 0, 586, 587, 1, 0, 0, 0, 587, 590, 1, 0, 0, 0, 588, 586, 1, 0, 0, 0, 589, 591, 5, 13, 0, 0, 590, 589, 1, 0, 0, 0, 590, 591, 1, 0, 0, 0, 591, 593, 1, 0, 0, 0, 592, 594, 5, 10, 0, 0, 593, 592, 1, 0, 0, 0, 593, 594, 1, 0, 0, 0, 594, 595, 1, 0, 0, 0, 595, 596, 6, 21, 11, 0, 596, 59, 1, 0, 0, 0, 597, 598, 5, 47, 0, 0, 598, 599, 5, 42, 0, 0, 599, 604, 1, 0, 0, 0, 600, 603, 3, 60, 22, 0, 601, 603, 9, 0, 0, 0, 602, 600, 1, 0, 0, 0, 602, 601, 1, 0, 0, 0, 603, 606, 1, 0, 0, 0, 604, 605, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 605, 607, 1, 0, 0, 0, 606, 604, 1, 0, 0, 0, 607, 608, 5, 42, 0, 0, 608, 609, 5, 47, 0, 0, 609, 610, 1, 0, 0, 0, 610, 611, 6, 22, 11, 0, 611, 61, 1, 0, 0, 0, 612, 614, 7, 23, 0, 0, 613, 612, 1, 0, 0, 0, 614, 615, 1, 0, 0, 0, 615, 613, 1, 0, 0, 0, 615, 616, 1, 0, 0, 0, 616, 617, 1, 0, 0, 0, 617, 618, 6, 23, 11, 0, 618, 63, 1, 0, 0, 0, 619, 623, 8, 24, 0, 0, 620, 621, 5, 47, 0, 0, 621, 623, 8, 25, 0, 0, 622, 619, 1, 0, 0, 0, 622, 620, 1, 0, 0, 0, 623, 65, 1, 0, 0, 0, 624, 626, 3, 64, 24, 0, 625, 624, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 625, 1, 0, 0, 0, 627, 628, 1, 0, 0, 0, 628, 67, 1, 0, 0, 0, 629, 630, 3, 178, 81, 0, 630, 631, 1, 0, 0, 0, 631, 632, 6, 26, 12, 0, 632, 633, 6, 26, 13, 0, 633, 69, 1, 0, 0, 0, 634, 635, 3, 78, 31, 0, 635, 636, 1, 0, 0, 0, 636, 637, 6, 27, 14, 0, 637, 638, 6, 27, 15, 0, 638, 71, 1, 0, 0, 0, 639, 640, 3, 62, 23, 0, 640, 641, 1, 0, 0, 0, 641, 642, 6, 28, 11, 0, 642, 73, 1, 0, 0, 0, 643, 644, 3, 58, 21, 0, 644, 645, 1, 0, 0, 0, 645, 646, 6, 29, 11, 0, 646, 75, 1, 0, 0, 0, 647, 648, 3, 60, 22, 0, 648, 649, 1, 0, 0, 0, 649, 650, 6, 30, 11, 0, 650, 77, 1, 0, 0, 0, 651, 652, 5, 124, 0, 0, 652, 653, 1, 0, 0, 0, 653, 654, 6, 31, 15, 0, 654, 79, 1, 0, 0, 0, 655, 656, 7, 26, 0, 0, 656, 81, 1, 0, 0, 0, 657, 658, 7, 27, 0, 0, 658, 83, 1, 0, 0, 0, 659, 660, 5, 92, 0, 0, 660, 661, 7, 28, 0, 0, 661, 85, 1, 0, 0, 0, 662, 663, 8, 29, 0, 0, 663, 87, 1, 0, 0, 0, 664, 666, 7, 3, 0, 0, 665, 667, 7, 30, 0, 0, 666, 665, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 669, 1, 0, 0, 0, 668, 670, 3, 80, 32, 0, 669, 668, 1, 0, 0, 0, 670, 671, 1, 0, 0, 0, 671, 669, 1, 0, 0, 0, 671, 672, 1, 0, 0, 0, 672, 89, 1, 0, 0, 0, 673, 674, 5, 64, 0, 0, 674, 91, 1, 0, 0, 0, 675, 676, 5, 96, 0, 0, 676, 93, 1, 0, 0, 0, 677, 681, 8, 31, 0, 0, 678, 679, 5, 96, 0, 0, 679, 681, 5, 96, 0, 0, 680, 677, 1, 0, 0, 0, 680, 678, 1, 0, 0, 0, 681, 95, 1, 0, 0, 0, 682, 683, 5, 95, 0, 0, 683, 97, 1, 0, 0, 0, 684, 688, 3, 82, 33, 0, 685, 688, 3, 80, 32, 0, 686, 688, 3, 96, 40, 0, 687, 684, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 687, 686, 1, 0, 0, 0, 688, 99, 1, 0, 0, 0, 689, 694, 5, 34, 0, 0, 690, 693, 3, 84, 34, 0, 691, 693, 3, 86, 35, 0, 692, 690, 1, 0, 0, 0, 692, 691, 1, 0, 0, 0, 693, 696, 1, 0, 0, 0, 694, 692, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 697, 1, 0, 0, 0, 696, 694, 1, 0, 0, 0, 697, 719, 5, 34, 0, 0, 698, 699, 5, 34, 0, 0, 699, 700, 5, 34, 0, 0, 700, 701, 5, 34, 0, 0, 701, 705, 1, 0, 0, 0, 702, 704, 8, 22, 0, 0, 703, 702, 1, 0, 0, 0, 704, 707, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 705, 703, 1, 0, 0, 0, 706, 708, 1, 0, 0, 0, 707, 705, 1, 0, 0, 0, 708, 709, 5, 34, 0, 0, 709, 710, 5, 34, 0, 0, 710, 711, 5, 34, 0, 0, 711, 713, 1, 0, 0, 0, 712, 714, 5, 34, 0, 0, 713, 712, 1, 0, 0, 0, 713, 714, 1, 0, 0, 0, 714, 716, 1, 0, 0, 0, 715, 717, 5, 34, 0, 0, 716, 715, 1, 0, 0, 0, 716, 717, 1, 0, 0, 0, 717, 719, 1, 0, 0, 0, 718, 689, 1, 0, 0, 0, 718, 698, 1, 0, 0, 0, 719, 101, 1, 0, 0, 0, 720, 722, 3, 80, 32, 0, 721, 720, 1, 0, 0, 0, 722, 723, 1, 0, 0, 0, 723, 721, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 103, 1, 0, 0, 0, 725, 727, 3, 80, 32, 0, 726, 725, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 726, 1, 0, 0, 0, 728, 729, 1, 0, 0, 0, 729, 730, 1, 0, 0, 0, 730, 734, 3, 120, 52, 0, 731, 733, 3, 80, 32, 0, 732, 731, 1, 0, 0, 0, 733, 736, 1, 0, 0, 0, 734, 732, 1, 0, 0, 0, 734, 735, 1, 0, 0, 0, 735, 768, 1, 0, 0, 0, 736, 734, 1, 0, 0, 0, 737, 739, 3, 120, 52, 0, 738, 740, 3, 80, 32, 0, 739, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 739, 1, 0, 0, 0, 741, 742, 1, 0, 0, 0, 742, 768, 1, 0, 0, 0, 743, 745, 3, 80, 32, 0, 744, 743, 1, 0, 0, 0, 745, 746, 1, 0, 0, 0, 746, 744, 1, 0, 0, 0, 746, 747, 1, 0, 0, 0, 747, 755, 1, 0, 0, 0, 748, 752, 3, 120, 52, 0, 749, 751, 3, 80, 32, 0, 750, 749, 1, 0, 0, 0, 751, 754, 1, 0, 0, 0, 752, 750, 1, 0, 0, 0, 752, 753, 1, 0, 0, 0, 753, 756, 1, 0, 0, 0, 754, 752, 1, 0, 0, 0, 755, 748, 1, 0, 0, 0, 755, 756, 1, 0, 0, 0, 756, 757, 1, 0, 0, 0, 757, 758, 3, 88, 36, 0, 758, 768, 1, 0, 0, 0, 759, 761, 3, 120, 52, 0, 760, 762, 3, 80, 32, 0, 761, 760, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 761, 1, 0, 0, 0, 763, 764, 1, 0, 0, 0, 764, 765, 1, 0, 0, 0, 765, 766, 3, 88, 36, 0, 766, 768, 1, 0, 0, 0, 767, 726, 1, 0, 0, 0, 767, 737, 1, 0, 0, 0, 767, 744, 1, 0, 0, 0, 767, 759, 1, 0, 0, 0, 768, 105, 1, 0, 0, 0, 769, 770, 7, 32, 0, 0, 770, 771, 7, 33, 0, 0, 771, 107, 1, 0, 0, 0, 772, 773, 7, 12, 0, 0, 773, 774, 7, 9, 0, 0, 774, 775, 7, 0, 0, 0, 775, 109, 1, 0, 0, 0, 776, 777, 7, 12, 0, 0, 777, 778, 7, 2, 0, 0, 778, 779, 7, 4, 0, 0, 779, 111, 1, 0, 0, 0, 780, 781, 5, 61, 0, 0, 781, 113, 1, 0, 0, 0, 782, 783, 5, 58, 0, 0, 783, 784, 5, 58, 0, 0, 784, 115, 1, 0, 0, 0, 785, 786, 5, 44, 0, 0, 786, 117, 1, 0, 0, 0, 787, 788, 7, 0, 0, 0, 788, 789, 7, 3, 0, 0, 789, 790, 7, 2, 0, 0, 790, 791, 7, 4, 0, 0, 791, 119, 1, 0, 0, 0, 792, 793, 5, 46, 0, 0, 793, 121, 1, 0, 0, 0, 794, 795, 7, 15, 0, 0, 795, 796, 7, 12, 0, 0, 796, 797, 7, 13, 0, 0, 797, 798, 7, 2, 0, 0, 798, 799, 7, 3, 0, 0, 799, 123, 1, 0, 0, 0, 800, 801, 7, 15, 0, 0, 801, 802, 7, 1, 0, 0, 802, 803, 7, 6, 0, 0, 803, 804, 7, 2, 0, 0, 804, 805, 7, 5, 0, 0, 805, 125, 1, 0, 0, 0, 806, 807, 7, 13, 0, 0, 807, 808, 7, 12, 0, 0, 808, 809, 7, 2, 0, 0, 809, 810, 7, 5, 0, 0, 810, 127, 1, 0, 0, 0, 811, 812, 5, 40, 0, 0, 812, 129, 1, 0, 0, 0, 813, 814, 7, 1, 0, 0, 814, 815, 7, 9, 0, 0, 815, 131, 1, 0, 0, 0, 816, 817, 7, 1, 0, 0, 817, 818, 7, 2, 0, 0, 818, 133, 1, 0, 0, 0, 819, 820, 7, 13, 0, 0, 820, 821, 7, 1, 0, 0, 821, 822, 7, 18, 0, 0, 822, 823, 7, 3, 0, 0, 823, 135, 1, 0, 0, 0, 824, 825, 7, 9, 0, 0, 825, 826, 7, 7, 0, 0, 826, 827, 7, 5, 0, 0, 827, 137, 1, 0, 0, 0, 828, 829, 7, 9, 0, 0, 829, 830, 7, 19, 0, 0, 830, 831, 7, 13, 0, 0, 831, 832, 7, 13, 0, 0, 832, 139, 1, 0, 0, 0, 833, 834, 7, 9, 0, 0, 834, 835, 7, 19, 0, 0, 835, 836, 7, 13, 0, 0, 836, 837, 7, 13, 0, 0, 837, 838, 7, 2, 0, 0, 838, 141, 1, 0, 0, 0, 839, 840, 7, 7, 0, 0, 840, 841, 7, 6, 0, 0, 841, 143, 1, 0, 0, 0, 842, 843, 5, 63, 0, 0, 843, 145, 1, 0, 0, 0, 844, 845, 7, 6, 0, 0, 845, 846, 7, 13, 0, 0, 846, 847, 7, 1, 0, 0, 847, 848, 7, 18, 0, 0, 848, 849, 7, 3, 0, 0, 849, 147, 1, 0, 0, 0, 850, 851, 5, 41, 0, 0, 851, 149, 1, 0, 0, 0, 852, 853, 7, 5, 0, 0, 853, 854, 7, 6, 0, 0, 854, 855, 7, 19, 0, 0, 855, 856, 7, 3, 0, 0, 856, 151, 1, 0, 0, 0, 857, 858, 5, 61, 0, 0, 858, 859, 5, 61, 0, 0, 859, 153, 1, 0, 0, 0, 860, 861, 5, 61, 0, 0, 861, 862, 5, 126, 0, 0, 862, 155, 1, 0, 0, 0, 863, 864, 5, 33, 0, 0, 864, 865, 5, 61, 0, 0, 865, 157, 1, 0, 0, 0, 866, 867, 5, 60, 0, 0, 867, 159, 1, 0, 0, 0, 868, 869, 5, 60, 0, 0, 869, 870, 5, 61, 0, 0, 870, 161, 1, 0, 0, 0, 871, 872, 5, 62, 0, 0, 872, 163, 1, 0, 0, 0, 873, 874, 5, 62, 0, 0, 874, 875, 5, 61, 0, 0, 875, 165, 1, 0, 0, 0, 876, 877, 5, 43, 0, 0, 877, 167, 1, 0, 0, 0, 878, 879, 5, 45, 0, 0, 879, 169, 1, 0, 0, 0, 880, 881, 5, 42, 0, 0, 881, 171, 1, 0, 0, 0, 882, 883, 5, 47, 0, 0, 883, 173, 1, 0, 0, 0, 884, 885, 5, 37, 0, 0, 885, 175, 1, 0, 0, 0, 886, 887, 3, 144, 64, 0, 887, 891, 3, 82, 33, 0, 888, 890, 3, 98, 41, 0, 889, 888, 1, 0, 0, 0, 890, 893, 1, 0, 0, 0, 891, 889, 1, 0, 0, 0, 891, 892, 1, 0, 0, 0, 892, 901, 1, 0, 0, 0, 893, 891, 1, 0, 0, 0, 894, 896, 3, 144, 64, 0, 895, 897, 3, 80, 32, 0, 896, 895, 1, 0, 0, 0, 897, 898, 1, 0, 0, 0, 898, 896, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 901, 1, 0, 0, 0, 900, 886, 1, 0, 0, 0, 900, 894, 1, 0, 0, 0, 901, 177, 1, 0, 0, 0, 902, 903, 5, 91, 0, 0, 903, 904, 1, 0, 0, 0, 904, 905, 6, 81, 0, 0, 905, 906, 6, 81, 0, 0, 906, 179, 1, 0, 0, 0, 907, 908, 5, 93, 0, 0, 908, 909, 1, 0, 0, 0, 909, 910, 6, 82, 15, 0, 910, 911, 6, 82, 15, 0, 911, 181, 1, 0, 0, 0, 912, 916, 3, 82, 33, 0, 913, 915, 3, 98, 41, 0, 914, 913, 1, 0, 0, 0, 915, 918, 1, 0, 0, 0, 916, 914, 1, 0, 0, 0, 916, 917, 1, 0, 0, 0, 917, 929, 1, 0, 0, 0, 918, 916, 1, 0, 0, 0, 919, 922, 3, 96, 40, 0, 920, 922, 3, 90, 37, 0, 921, 919, 1, 0, 0, 0, 921, 920, 1, 0, 0, 0, 922, 924, 1, 0, 0, 0, 923, 925, 3, 98, 41, 0, 924, 923, 1, 0, 0, 0, 925, 926, 1, 0, 0, 0, 926, 924, 1, 0, 0, 0, 926, 927, 1, 0, 0, 0, 927, 929, 1, 0, 0, 0, 928, 912, 1, 0, 0, 0, 928, 921, 1, 0, 0, 0, 929, 183, 1, 0, 0, 0, 930, 932, 3, 92, 38, 0, 931, 933, 3, 94, 39, 0, 932, 931, 1, 0, 0, 0, 933, 934, 1, 0, 0, 0, 934, 932, 1, 0, 0, 0, 934, 935, 1, 0, 0, 0, 935, 936, 1, 0, 0, 0, 936, 937, 3, 92, 38, 0, 937, 185, 1, 0, 0, 0, 938, 939, 3, 184, 84, 0, 939, 187, 1, 0, 0, 0, 940, 941, 3, 58, 21, 0, 941, 942, 1, 0, 0, 0, 942, 943, 6, 86, 11, 0, 943, 189, 1, 0, 0, 0, 944, 945, 3, 60, 22, 0, 945, 946, 1, 0, 0, 0, 946, 947, 6, 87, 11, 0, 947, 191, 1, 0, 0, 0, 948, 949, 3, 62, 23, 0, 949, 950, 1, 0, 0, 0, 950, 951, 6, 88, 11, 0, 951, 193, 1, 0, 0, 0, 952, 953, 3, 78, 31, 0, 953, 954, 1, 0, 0, 0, 954, 955, 6, 89, 14, 0, 955, 956, 6, 89, 15, 0, 956, 195, 1, 0, 0, 0, 957, 958, 3, 178, 81, 0, 958, 959, 1, 0, 0, 0, 959, 960, 6, 90, 12, 0, 960, 197, 1, 0, 0, 0, 961, 962, 3, 180, 82, 0, 962, 963, 1, 0, 0, 0, 963, 964, 6, 91, 16, 0, 964, 199, 1, 0, 0, 0, 965, 966, 3, 364, 174, 0, 966, 967, 1, 0, 0, 0, 967, 968, 6, 92, 17, 0, 968, 201, 1, 0, 0, 0, 969, 970, 3, 116, 50, 0, 970, 971, 1, 0, 0, 0, 971, 972, 6, 93, 18, 0, 972, 203, 1, 0, 0, 0, 973, 974, 3, 112, 48, 0, 974, 975, 1, 0, 0, 0, 975, 976, 6, 94, 19, 0, 976, 205, 1, 0, 0, 0, 977, 978, 7, 16, 0, 0, 978, 979, 7, 3, 0, 0, 979, 980, 7, 5, 0, 0, 980, 981, 7, 12, 0, 0, 981, 982, 7, 0, 0, 0, 982, 983, 7, 12, 0, 0, 983, 984, 7, 5, 0, 0, 984, 985, 7, 12, 0, 0, 985, 207, 1, 0, 0, 0, 986, 987, 3, 66, 25, 0, 987, 988, 1, 0, 0, 0, 988, 989, 6, 96, 20, 0, 989, 209, 1, 0, 0, 0, 990, 991, 3, 100, 42, 0, 991, 992, 1, 0, 0, 0, 992, 993, 6, 97, 21, 0, 993, 211, 1, 0, 0, 0, 994, 995, 3, 58, 21, 0, 995, 996, 1, 0, 0, 0, 996, 997, 6, 98, 11, 0, 997, 213, 1, 0, 0, 0, 998, 999, 3, 60, 22, 0, 999, 1000, 1, 0, 0, 0, 1000, 1001, 6, 99, 11, 0, 1001, 215, 1, 0, 0, 0, 1002, 1003, 3, 62, 23, 0, 1003, 1004, 1, 0, 0, 0, 1004, 1005, 6, 100, 11, 0, 1005, 217, 1, 0, 0, 0, 1006, 1007, 3, 78, 31, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1009, 6, 101, 14, 0, 1009, 1010, 6, 101, 15, 0, 1010, 219, 1, 0, 0, 0, 1011, 1012, 3, 120, 52, 0, 1012, 1013, 1, 0, 0, 0, 1013, 1014, 6, 102, 22, 0, 1014, 221, 1, 0, 0, 0, 1015, 1016, 3, 116, 50, 0, 1016, 1017, 1, 0, 0, 0, 1017, 1018, 6, 103, 18, 0, 1018, 223, 1, 0, 0, 0, 1019, 1024, 3, 82, 33, 0, 1020, 1024, 3, 80, 32, 0, 1021, 1024, 3, 96, 40, 0, 1022, 1024, 3, 170, 77, 0, 1023, 1019, 1, 0, 0, 0, 1023, 1020, 1, 0, 0, 0, 1023, 1021, 1, 0, 0, 0, 1023, 1022, 1, 0, 0, 0, 1024, 225, 1, 0, 0, 0, 1025, 1028, 3, 82, 33, 0, 1026, 1028, 3, 170, 77, 0, 1027, 1025, 1, 0, 0, 0, 1027, 1026, 1, 0, 0, 0, 1028, 1032, 1, 0, 0, 0, 1029, 1031, 3, 224, 104, 0, 1030, 1029, 1, 0, 0, 0, 1031, 1034, 1, 0, 0, 0, 1032, 1030, 1, 0, 0, 0, 1032, 1033, 1, 0, 0, 0, 1033, 1045, 1, 0, 0, 0, 1034, 1032, 1, 0, 0, 0, 1035, 1038, 3, 96, 40, 0, 1036, 1038, 3, 90, 37, 0, 1037, 1035, 1, 0, 0, 0, 1037, 1036, 1, 0, 0, 0, 1038, 1040, 1, 0, 0, 0, 1039, 1041, 3, 224, 104, 0, 1040, 1039, 1, 0, 0, 0, 1041, 1042, 1, 0, 0, 0, 1042, 1040, 1, 0, 0, 0, 1042, 1043, 1, 0, 0, 0, 1043, 1045, 1, 0, 0, 0, 1044, 1027, 1, 0, 0, 0, 1044, 1037, 1, 0, 0, 0, 1045, 227, 1, 0, 0, 0, 1046, 1049, 3, 226, 105, 0, 1047, 1049, 3, 184, 84, 0, 1048, 1046, 1, 0, 0, 0, 1048, 1047, 1, 0, 0, 0, 1049, 1050, 1, 0, 0, 0, 1050, 1048, 1, 0, 0, 0, 1050, 1051, 1, 0, 0, 0, 1051, 229, 1, 0, 0, 0, 1052, 1053, 3, 58, 21, 0, 1053, 1054, 1, 0, 0, 0, 1054, 1055, 6, 107, 11, 0, 1055, 231, 1, 0, 0, 0, 1056, 1057, 3, 60, 22, 0, 1057, 1058, 1, 0, 0, 0, 1058, 1059, 6, 108, 11, 0, 1059, 233, 1, 0, 0, 0, 1060, 1061, 3, 62, 23, 0, 1061, 1062, 1, 0, 0, 0, 1062, 1063, 6, 109, 11, 0, 1063, 235, 1, 0, 0, 0, 1064, 1065, 3, 78, 31, 0, 1065, 1066, 1, 0, 0, 0, 1066, 1067, 6, 110, 14, 0, 1067, 1068, 6, 110, 15, 0, 1068, 237, 1, 0, 0, 0, 1069, 1070, 3, 112, 48, 0, 1070, 1071, 1, 0, 0, 0, 1071, 1072, 6, 111, 19, 0, 1072, 239, 1, 0, 0, 0, 1073, 1074, 3, 116, 50, 0, 1074, 1075, 1, 0, 0, 0, 1075, 1076, 6, 112, 18, 0, 1076, 241, 1, 0, 0, 0, 1077, 1078, 3, 120, 52, 0, 1078, 1079, 1, 0, 0, 0, 1079, 1080, 6, 113, 22, 0, 1080, 243, 1, 0, 0, 0, 1081, 1082, 7, 12, 0, 0, 1082, 1083, 7, 2, 0, 0, 1083, 245, 1, 0, 0, 0, 1084, 1085, 3, 228, 106, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1087, 6, 115, 23, 0, 1087, 247, 1, 0, 0, 0, 1088, 1089, 3, 58, 21, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1091, 6, 116, 11, 0, 1091, 249, 1, 0, 0, 0, 1092, 1093, 3, 60, 22, 0, 1093, 1094, 1, 0, 0, 0, 1094, 1095, 6, 117, 11, 0, 1095, 251, 1, 0, 0, 0, 1096, 1097, 3, 62, 23, 0, 1097, 1098, 1, 0, 0, 0, 1098, 1099, 6, 118, 11, 0, 1099, 253, 1, 0, 0, 0, 1100, 1101, 3, 78, 31, 0, 1101, 1102, 1, 0, 0, 0, 1102, 1103, 6, 119, 14, 0, 1103, 1104, 6, 119, 15, 0, 1104, 255, 1, 0, 0, 0, 1105, 1106, 3, 178, 81, 0, 1106, 1107, 1, 0, 0, 0, 1107, 1108, 6, 120, 12, 0, 1108, 1109, 6, 120, 24, 0, 1109, 257, 1, 0, 0, 0, 1110, 1111, 7, 7, 0, 0, 1111, 1112, 7, 9, 0, 0, 1112, 1113, 1, 0, 0, 0, 1113, 1114, 6, 121, 25, 0, 1114, 259, 1, 0, 0, 0, 1115, 1116, 7, 20, 0, 0, 1116, 1117, 7, 1, 0, 0, 1117, 1118, 7, 5, 0, 0, 1118, 1119, 7, 10, 0, 0, 1119, 1120, 1, 0, 0, 0, 1120, 1121, 6, 122, 25, 0, 1121, 261, 1, 0, 0, 0, 1122, 1123, 8, 34, 0, 0, 1123, 263, 1, 0, 0, 0, 1124, 1126, 3, 262, 123, 0, 1125, 1124, 1, 0, 0, 0, 1126, 1127, 1, 0, 0, 0, 1127, 1125, 1, 0, 0, 0, 1127, 1128, 1, 0, 0, 0, 1128, 1129, 1, 0, 0, 0, 1129, 1130, 3, 364, 174, 0, 1130, 1132, 1, 0, 0, 0, 1131, 1125, 1, 0, 0, 0, 1131, 1132, 1, 0, 0, 0, 1132, 1134, 1, 0, 0, 0, 1133, 1135, 3, 262, 123, 0, 1134, 1133, 1, 0, 0, 0, 1135, 1136, 1, 0, 0, 0, 1136, 1134, 1, 0, 0, 0, 1136, 1137, 1, 0, 0, 0, 1137, 265, 1, 0, 0, 0, 1138, 1139, 3, 264, 124, 0, 1139, 1140, 1, 0, 0, 0, 1140, 1141, 6, 125, 26, 0, 1141, 267, 1, 0, 0, 0, 1142, 1143, 3, 58, 21, 0, 1143, 1144, 1, 0, 0, 0, 1144, 1145, 6, 126, 11, 0, 1145, 269, 1, 0, 0, 0, 1146, 1147, 3, 60, 22, 0, 1147, 1148, 1, 0, 0, 0, 1148, 1149, 6, 127, 11, 0, 1149, 271, 1, 0, 0, 0, 1150, 1151, 3, 62, 23, 0, 1151, 1152, 1, 0, 0, 0, 1152, 1153, 6, 128, 11, 0, 1153, 273, 1, 0, 0, 0, 1154, 1155, 3, 78, 31, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1157, 6, 129, 14, 0, 1157, 1158, 6, 129, 15, 0, 1158, 1159, 6, 129, 15, 0, 1159, 275, 1, 0, 0, 0, 1160, 1161, 3, 112, 48, 0, 1161, 1162, 1, 0, 0, 0, 1162, 1163, 6, 130, 19, 0, 1163, 277, 1, 0, 0, 0, 1164, 1165, 3, 116, 50, 0, 1165, 1166, 1, 0, 0, 0, 1166, 1167, 6, 131, 18, 0, 1167, 279, 1, 0, 0, 0, 1168, 1169, 3, 120, 52, 0, 1169, 1170, 1, 0, 0, 0, 1170, 1171, 6, 132, 22, 0, 1171, 281, 1, 0, 0, 0, 1172, 1173, 3, 260, 122, 0, 1173, 1174, 1, 0, 0, 0, 1174, 1175, 6, 133, 27, 0, 1175, 283, 1, 0, 0, 0, 1176, 1177, 3, 228, 106, 0, 1177, 1178, 1, 0, 0, 0, 1178, 1179, 6, 134, 23, 0, 1179, 285, 1, 0, 0, 0, 1180, 1181, 3, 186, 85, 0, 1181, 1182, 1, 0, 0, 0, 1182, 1183, 6, 135, 28, 0, 1183, 287, 1, 0, 0, 0, 1184, 1185, 3, 58, 21, 0, 1185, 1186, 1, 0, 0, 0, 1186, 1187, 6, 136, 11, 0, 1187, 289, 1, 0, 0, 0, 1188, 1189, 3, 60, 22, 0, 1189, 1190, 1, 0, 0, 0, 1190, 1191, 6, 137, 11, 0, 1191, 291, 1, 0, 0, 0, 1192, 1193, 3, 62, 23, 0, 1193, 1194, 1, 0, 0, 0, 1194, 1195, 6, 138, 11, 0, 1195, 293, 1, 0, 0, 0, 1196, 1197, 3, 78, 31, 0, 1197, 1198, 1, 0, 0, 0, 1198, 1199, 6, 139, 14, 0, 1199, 1200, 6, 139, 15, 0, 1200, 295, 1, 0, 0, 0, 1201, 1202, 3, 364, 174, 0, 1202, 1203, 1, 0, 0, 0, 1203, 1204, 6, 140, 17, 0, 1204, 297, 1, 0, 0, 0, 1205, 1206, 3, 116, 50, 0, 1206, 1207, 1, 0, 0, 0, 1207, 1208, 6, 141, 18, 0, 1208, 299, 1, 0, 0, 0, 1209, 1210, 3, 120, 52, 0, 1210, 1211, 1, 0, 0, 0, 1211, 1212, 6, 142, 22, 0, 1212, 301, 1, 0, 0, 0, 1213, 1214, 3, 258, 121, 0, 1214, 1215, 1, 0, 0, 0, 1215, 1216, 6, 143, 29, 0, 1216, 1217, 6, 143, 30, 0, 1217, 303, 1, 0, 0, 0, 1218, 1219, 3, 66, 25, 0, 1219, 1220, 1, 0, 0, 0, 1220, 1221, 6, 144, 20, 0, 1221, 305, 1, 0, 0, 0, 1222, 1223, 3, 100, 42, 0, 1223, 1224, 1, 0, 0, 0, 1224, 1225, 6, 145, 21, 0, 1225, 307, 1, 0, 0, 0, 1226, 1227, 3, 58, 21, 0, 1227, 1228, 1, 0, 0, 0, 1228, 1229, 6, 146, 11, 0, 1229, 309, 1, 0, 0, 0, 1230, 1231, 3, 60, 22, 0, 1231, 1232, 1, 0, 0, 0, 1232, 1233, 6, 147, 11, 0, 1233, 311, 1, 0, 0, 0, 1234, 1235, 3, 62, 23, 0, 1235, 1236, 1, 0, 0, 0, 1236, 1237, 6, 148, 11, 0, 1237, 313, 1, 0, 0, 0, 1238, 1239, 3, 78, 31, 0, 1239, 1240, 1, 0, 0, 0, 1240, 1241, 6, 149, 14, 0, 1241, 1242, 6, 149, 15, 0, 1242, 1243, 6, 149, 15, 0, 1243, 315, 1, 0, 0, 0, 1244, 1245, 3, 116, 50, 0, 1245, 1246, 1, 0, 0, 0, 1246, 1247, 6, 150, 18, 0, 1247, 317, 1, 0, 0, 0, 1248, 1249, 3, 120, 52, 0, 1249, 1250, 1, 0, 0, 0, 1250, 1251, 6, 151, 22, 0, 1251, 319, 1, 0, 0, 0, 1252, 1253, 3, 228, 106, 0, 1253, 1254, 1, 0, 0, 0, 1254, 1255, 6, 152, 23, 0, 1255, 321, 1, 0, 0, 0, 1256, 1257, 3, 58, 21, 0, 1257, 1258, 1, 0, 0, 0, 1258, 1259, 6, 153, 11, 0, 1259, 323, 1, 0, 0, 0, 1260, 1261, 3, 60, 22, 0, 1261, 1262, 1, 0, 0, 0, 1262, 1263, 6, 154, 11, 0, 1263, 325, 1, 0, 0, 0, 1264, 1265, 3, 62, 23, 0, 1265, 1266, 1, 0, 0, 0, 1266, 1267, 6, 155, 11, 0, 1267, 327, 1, 0, 0, 0, 1268, 1269, 3, 78, 31, 0, 1269, 1270, 1, 0, 0, 0, 1270, 1271, 6, 156, 14, 0, 1271, 1272, 6, 156, 15, 0, 1272, 329, 1, 0, 0, 0, 1273, 1274, 3, 120, 52, 0, 1274, 1275, 1, 0, 0, 0, 1275, 1276, 6, 157, 22, 0, 1276, 331, 1, 0, 0, 0, 1277, 1278, 3, 186, 85, 0, 1278, 1279, 1, 0, 0, 0, 1279, 1280, 6, 158, 28, 0, 1280, 333, 1, 0, 0, 0, 1281, 1282, 3, 182, 83, 0, 1282, 1283, 1, 0, 0, 0, 1283, 1284, 6, 159, 31, 0, 1284, 335, 1, 0, 0, 0, 1285, 1286, 3, 58, 21, 0, 1286, 1287, 1, 0, 0, 0, 1287, 1288, 6, 160, 11, 0, 1288, 337, 1, 0, 0, 0, 1289, 1290, 3, 60, 22, 0, 1290, 1291, 1, 0, 0, 0, 1291, 1292, 6, 161, 11, 0, 1292, 339, 1, 0, 0, 0, 1293, 1294, 3, 62, 23, 0, 1294, 1295, 1, 0, 0, 0, 1295, 1296, 6, 162, 11, 0, 1296, 341, 1, 0, 0, 0, 1297, 1298, 3, 78, 31, 0, 1298, 1299, 1, 0, 0, 0, 1299, 1300, 6, 163, 14, 0, 1300, 1301, 6, 163, 15, 0, 1301, 343, 1, 0, 0, 0, 1302, 1303, 7, 1, 0, 0, 1303, 1304, 7, 9, 0, 0, 1304, 1305, 7, 15, 0, 0, 1305, 1306, 7, 7, 0, 0, 1306, 345, 1, 0, 0, 0, 1307, 1308, 3, 58, 21, 0, 1308, 1309, 1, 0, 0, 0, 1309, 1310, 6, 165, 11, 0, 1310, 347, 1, 0, 0, 0, 1311, 1312, 3, 60, 22, 0, 1312, 1313, 1, 0, 0, 0, 1313, 1314, 6, 166, 11, 0, 1314, 349, 1, 0, 0, 0, 1315, 1316, 3, 62, 23, 0, 1316, 1317, 1, 0, 0, 0, 1317, 1318, 6, 167, 11, 0, 1318, 351, 1, 0, 0, 0, 1319, 1320, 3, 78, 31, 0, 1320, 1321, 1, 0, 0, 0, 1321, 1322, 6, 168, 14, 0, 1322, 1323, 6, 168, 15, 0, 1323, 353, 1, 0, 0, 0, 1324, 1325, 7, 15, 0, 0, 1325, 1326, 7, 19, 0, 0, 1326, 1327, 7, 9, 0, 0, 1327, 1328, 7, 4, 0, 0, 1328, 1329, 7, 5, 0, 0, 1329, 1330, 7, 1, 0, 0, 1330, 1331, 7, 7, 0, 0, 1331, 1332, 7, 9, 0, 0, 1332, 1333, 7, 2, 0, 0, 1333, 355, 1, 0, 0, 0, 1334, 1335, 3, 58, 21, 0, 1335, 1336, 1, 0, 0, 0, 1336, 1337, 6, 170, 11, 0, 1337, 357, 1, 0, 0, 0, 1338, 1339, 3, 60, 22, 0, 1339, 1340, 1, 0, 0, 0, 1340, 1341, 6, 171, 11, 0, 1341, 359, 1, 0, 0, 0, 1342, 1343, 3, 62, 23, 0, 1343, 1344, 1, 0, 0, 0, 1344, 1345, 6, 172, 11, 0, 1345, 361, 1, 0, 0, 0, 1346, 1347, 3, 180, 82, 0, 1347, 1348, 1, 0, 0, 0, 1348, 1349, 6, 173, 16, 0, 1349, 1350, 6, 173, 15, 0, 1350, 363, 1, 0, 0, 0, 1351, 1352, 5, 58, 0, 0, 1352, 365, 1, 0, 0, 0, 1353, 1359, 3, 90, 37, 0, 1354, 1359, 3, 80, 32, 0, 1355, 1359, 3, 120, 52, 0, 1356, 1359, 3, 82, 33, 0, 1357, 1359, 3, 96, 40, 0, 1358, 1353, 1, 0, 0, 0, 1358, 1354, 1, 0, 0, 0, 1358, 1355, 1, 0, 0, 0, 1358, 1356, 1, 0, 0, 0, 1358, 1357, 1, 0, 0, 0, 1359, 1360, 1, 0, 0, 0, 1360, 1358, 1, 0, 0, 0, 1360, 1361, 1, 0, 0, 0, 1361, 367, 1, 0, 0, 0, 1362, 1363, 3, 58, 21, 0, 1363, 1364, 1, 0, 0, 0, 1364, 1365, 6, 176, 11, 0, 1365, 369, 1, 0, 0, 0, 1366, 1367, 3, 60, 22, 0, 1367, 1368, 1, 0, 0, 0, 1368, 1369, 6, 177, 11, 0, 1369, 371, 1, 0, 0, 0, 1370, 1371, 3, 62, 23, 0, 1371, 1372, 1, 0, 0, 0, 1372, 1373, 6, 178, 11, 0, 1373, 373, 1, 0, 0, 0, 1374, 1375, 3, 78, 31, 0, 1375, 1376, 1, 0, 0, 0, 1376, 1377, 6, 179, 14, 0, 1377, 1378, 6, 179, 15, 0, 1378, 375, 1, 0, 0, 0, 1379, 1380, 3, 66, 25, 0, 1380, 1381, 1, 0, 0, 0, 1381, 1382, 6, 180, 20, 0, 1382, 1383, 6, 180, 15, 0, 1383, 1384, 6, 180, 32, 0, 1384, 377, 1, 0, 0, 0, 1385, 1386, 3, 100, 42, 0, 1386, 1387, 1, 0, 0, 0, 1387, 1388, 6, 181, 21, 0, 1388, 1389, 6, 181, 15, 0, 1389, 1390, 6, 181, 32, 0, 1390, 379, 1, 0, 0, 0, 1391, 1392, 3, 58, 21, 0, 1392, 1393, 1, 0, 0, 0, 1393, 1394, 6, 182, 11, 0, 1394, 381, 1, 0, 0, 0, 1395, 1396, 3, 60, 22, 0, 1396, 1397, 1, 0, 0, 0, 1397, 1398, 6, 183, 11, 0, 1398, 383, 1, 0, 0, 0, 1399, 1400, 3, 62, 23, 0, 1400, 1401, 1, 0, 0, 0, 1401, 1402, 6, 184, 11, 0, 1402, 385, 1, 0, 0, 0, 1403, 1404, 3, 364, 174, 0, 1404, 1405, 1, 0, 0, 0, 1405, 1406, 6, 185, 17, 0, 1406, 1407, 6, 185, 15, 0, 1407, 1408, 6, 185, 7, 0, 1408, 387, 1, 0, 0, 0, 1409, 1410, 3, 116, 50, 0, 1410, 1411, 1, 0, 0, 0, 1411, 1412, 6, 186, 18, 0, 1412, 1413, 6, 186, 15, 0, 1413, 1414, 6, 186, 7, 0, 1414, 389, 1, 0, 0, 0, 1415, 1416, 3, 58, 21, 0, 1416, 1417, 1, 0, 0, 0, 1417, 1418, 6, 187, 11, 0, 1418, 391, 1, 0, 0, 0, 1419, 1420, 3, 60, 22, 0, 1420, 1421, 1, 0, 0, 0, 1421, 1422, 6, 188, 11, 0, 1422, 393, 1, 0, 0, 0, 1423, 1424, 3, 62, 23, 0, 1424, 1425, 1, 0, 0, 0, 1425, 1426, 6, 189, 11, 0, 1426, 395, 1, 0, 0, 0, 1427, 1428, 3, 186, 85, 0, 1428, 1429, 1, 0, 0, 0, 1429, 1430, 6, 190, 15, 0, 1430, 1431, 6, 190, 0, 0, 1431, 1432, 6, 190, 28, 0, 1432, 397, 1, 0, 0, 0, 1433, 1434, 3, 182, 83, 0, 1434, 1435, 1, 0, 0, 0, 1435, 1436, 6, 191, 15, 0, 1436, 1437, 6, 191, 0, 0, 1437, 1438, 6, 191, 31, 0, 1438, 399, 1, 0, 0, 0, 1439, 1440, 3, 106, 45, 0, 1440, 1441, 1, 0, 0, 0, 1441, 1442, 6, 192, 15, 0, 1442, 1443, 6, 192, 0, 0, 1443, 1444, 6, 192, 33, 0, 1444, 401, 1, 0, 0, 0, 1445, 1446, 3, 78, 31, 0, 1446, 1447, 1, 0, 0, 0, 1447, 1448, 6, 193, 14, 0, 1448, 1449, 6, 193, 15, 0, 1449, 403, 1, 0, 0, 0, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 576, 586, 590, 593, 602, 604, 615, 622, 627, 666, 671, 680, 687, 692, 694, 705, 713, 716, 718, 723, 728, 734, 741, 746, 752, 755, 763, 767, 891, 898, 900, 916, 921, 926, 928, 934, 1023, 1027, 1032, 1037, 1042, 1044, 1048, 1050, 1127, 1131, 1136, 1358, 1360, 34, 5, 2, 0, 5, 4, 0, 5, 6, 0, 5, 1, 0, 5, 3, 0, 5, 8, 0, 5, 12, 0, 5, 14, 0, 5, 10, 0, 5, 5, 0, 5, 11, 0, 0, 1, 0, 7, 69, 0, 5, 0, 0, 7, 29, 0, 4, 0, 0, 7, 70, 0, 7, 114, 0, 7, 38, 0, 7, 36, 0, 7, 25, 0, 7, 30, 0, 7, 40, 0, 7, 80, 0, 5, 13, 0, 5, 7, 0, 7, 90, 0, 7, 89, 0, 7, 72, 0, 7, 88, 0, 5, 9, 0, 7, 71, 0, 5, 15, 0, 7, 33, 0] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens index 04798fc3dca8a..63eb3a86419a3 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens @@ -22,7 +22,7 @@ UNKNOWN_CMD=21 LINE_COMMENT=22 MULTILINE_COMMENT=23 WS=24 -INDEX_UNQUOTED_IDENTIFIER=25 +UNQUOTED_SOURCE=25 EXPLAIN_WS=26 EXPLAIN_LINE_COMMENT=27 EXPLAIN_MULTILINE_COMMENT=28 diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts index ff985e02ccf1d..ab1f2d1a3b4d5 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts @@ -37,7 +37,7 @@ export default class esql_lexer extends Lexer { public static readonly LINE_COMMENT = 22; public static readonly MULTILINE_COMMENT = 23; public static readonly WS = 24; - public static readonly INDEX_UNQUOTED_IDENTIFIER = 25; + public static readonly UNQUOTED_SOURCE = 25; public static readonly EXPLAIN_WS = 26; public static readonly EXPLAIN_LINE_COMMENT = 27; public static readonly EXPLAIN_MULTILINE_COMMENT = 28; @@ -230,7 +230,7 @@ export default class esql_lexer extends Lexer { "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", - "WS", "INDEX_UNQUOTED_IDENTIFIER", + "WS", "UNQUOTED_SOURCE", "EXPLAIN_WS", "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", @@ -321,7 +321,7 @@ export default class esql_lexer extends Lexer { "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", "FROM", "GROK", "INLINESTATS", "KEEP", "LIMIT", "LOOKUP", "META", "METRICS", "MV_EXPAND", "RENAME", "ROW", "SHOW", "SORT", "STATS", "WHERE", "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", - "WS", "INDEX_UNQUOTED_IDENTIFIER_PART", "INDEX_UNQUOTED_IDENTIFIER", "EXPLAIN_OPENING_BRACKET", + "WS", "UNQUOTED_SOURCE_PART", "UNQUOTED_SOURCE", "EXPLAIN_OPENING_BRACKET", "EXPLAIN_PIPE", "EXPLAIN_WS", "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", "UNESCAPED_CHARS", "EXPONENT", "ASPERAND", "BACKQUOTE", "BACKQUOTE_BLOCK", "UNDERSCORE", "UNQUOTED_ID_BODY", @@ -332,32 +332,33 @@ export default class esql_lexer extends Lexer { "ASTERISK", "SLASH", "PERCENT", "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", "QUOTED_ID", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "FROM_PIPE", - "FROM_OPENING_BRACKET", "FROM_CLOSING_BRACKET", "FROM_COMMA", "FROM_ASSIGN", - "FROM_QUOTED_STRING", "METADATA", "FROM_INDEX_UNQUOTED_IDENTIFIER", "FROM_LINE_COMMENT", - "FROM_MULTILINE_COMMENT", "FROM_WS", "PROJECT_PIPE", "PROJECT_DOT", "PROJECT_COMMA", - "UNQUOTED_ID_BODY_WITH_PATTERN", "UNQUOTED_ID_PATTERN", "ID_PATTERN", - "PROJECT_LINE_COMMENT", "PROJECT_MULTILINE_COMMENT", "PROJECT_WS", "RENAME_PIPE", - "RENAME_ASSIGN", "RENAME_COMMA", "RENAME_DOT", "AS", "RENAME_ID_PATTERN", + "FROM_OPENING_BRACKET", "FROM_CLOSING_BRACKET", "FROM_COLON", "FROM_COMMA", + "FROM_ASSIGN", "METADATA", "FROM_UNQUOTED_SOURCE", "FROM_QUOTED_SOURCE", + "FROM_LINE_COMMENT", "FROM_MULTILINE_COMMENT", "FROM_WS", "PROJECT_PIPE", + "PROJECT_DOT", "PROJECT_COMMA", "UNQUOTED_ID_BODY_WITH_PATTERN", "UNQUOTED_ID_PATTERN", + "ID_PATTERN", "PROJECT_LINE_COMMENT", "PROJECT_MULTILINE_COMMENT", "PROJECT_WS", + "RENAME_PIPE", "RENAME_ASSIGN", "RENAME_COMMA", "RENAME_DOT", "AS", "RENAME_ID_PATTERN", "RENAME_LINE_COMMENT", "RENAME_MULTILINE_COMMENT", "RENAME_WS", "ENRICH_PIPE", "ENRICH_OPENING_BRACKET", "ON", "WITH", "ENRICH_POLICY_NAME_BODY", "ENRICH_POLICY_NAME", - "ENRICH_QUOTED_IDENTIFIER", "ENRICH_MODE_UNQUOTED_VALUE", "ENRICH_LINE_COMMENT", - "ENRICH_MULTILINE_COMMENT", "ENRICH_WS", "ENRICH_FIELD_PIPE", "ENRICH_FIELD_ASSIGN", - "ENRICH_FIELD_COMMA", "ENRICH_FIELD_DOT", "ENRICH_FIELD_WITH", "ENRICH_FIELD_ID_PATTERN", - "ENRICH_FIELD_QUOTED_IDENTIFIER", "ENRICH_FIELD_LINE_COMMENT", "ENRICH_FIELD_MULTILINE_COMMENT", - "ENRICH_FIELD_WS", "LOOKUP_PIPE", "LOOKUP_COMMA", "LOOKUP_DOT", "LOOKUP_ON", - "LOOKUP_INDEX_UNQUOTED_IDENTIFIER", "LOOKUP_LINE_COMMENT", "LOOKUP_MULTILINE_COMMENT", - "LOOKUP_WS", "LOOKUP_FIELD_PIPE", "LOOKUP_FIELD_COMMA", "LOOKUP_FIELD_DOT", - "LOOKUP_FIELD_ID_PATTERN", "LOOKUP_FIELD_LINE_COMMENT", "LOOKUP_FIELD_MULTILINE_COMMENT", - "LOOKUP_FIELD_WS", "MVEXPAND_PIPE", "MVEXPAND_DOT", "MVEXPAND_QUOTED_IDENTIFIER", - "MVEXPAND_UNQUOTED_IDENTIFIER", "MVEXPAND_LINE_COMMENT", "MVEXPAND_MULTILINE_COMMENT", - "MVEXPAND_WS", "SHOW_PIPE", "INFO", "SHOW_LINE_COMMENT", "SHOW_MULTILINE_COMMENT", - "SHOW_WS", "META_PIPE", "FUNCTIONS", "META_LINE_COMMENT", "META_MULTILINE_COMMENT", + "ENRICH_MODE_UNQUOTED_VALUE", "ENRICH_LINE_COMMENT", "ENRICH_MULTILINE_COMMENT", + "ENRICH_WS", "ENRICH_FIELD_PIPE", "ENRICH_FIELD_ASSIGN", "ENRICH_FIELD_COMMA", + "ENRICH_FIELD_DOT", "ENRICH_FIELD_WITH", "ENRICH_FIELD_ID_PATTERN", "ENRICH_FIELD_QUOTED_IDENTIFIER", + "ENRICH_FIELD_LINE_COMMENT", "ENRICH_FIELD_MULTILINE_COMMENT", "ENRICH_FIELD_WS", + "LOOKUP_PIPE", "LOOKUP_COLON", "LOOKUP_COMMA", "LOOKUP_DOT", "LOOKUP_ON", + "LOOKUP_UNQUOTED_SOURCE", "LOOKUP_QUOTED_SOURCE", "LOOKUP_LINE_COMMENT", + "LOOKUP_MULTILINE_COMMENT", "LOOKUP_WS", "LOOKUP_FIELD_PIPE", "LOOKUP_FIELD_COMMA", + "LOOKUP_FIELD_DOT", "LOOKUP_FIELD_ID_PATTERN", "LOOKUP_FIELD_LINE_COMMENT", + "LOOKUP_FIELD_MULTILINE_COMMENT", "LOOKUP_FIELD_WS", "MVEXPAND_PIPE", + "MVEXPAND_DOT", "MVEXPAND_QUOTED_IDENTIFIER", "MVEXPAND_UNQUOTED_IDENTIFIER", + "MVEXPAND_LINE_COMMENT", "MVEXPAND_MULTILINE_COMMENT", "MVEXPAND_WS", + "SHOW_PIPE", "INFO", "SHOW_LINE_COMMENT", "SHOW_MULTILINE_COMMENT", "SHOW_WS", + "META_PIPE", "FUNCTIONS", "META_LINE_COMMENT", "META_MULTILINE_COMMENT", "META_WS", "SETTING_CLOSING_BRACKET", "COLON", "SETTING", "SETTING_LINE_COMMENT", - "SETTTING_MULTILINE_COMMENT", "SETTING_WS", "METRICS_PIPE", "METRICS_INDEX_UNQUOTED_IDENTIFIER", - "METRICS_LINE_COMMENT", "METRICS_MULTILINE_COMMENT", "METRICS_WS", "CLOSING_METRICS_COMMA", - "CLOSING_METRICS_LINE_COMMENT", "CLOSING_METRICS_MULTILINE_COMMENT", "CLOSING_METRICS_WS", - "CLOSING_METRICS_QUOTED_IDENTIFIER", "CLOSING_METRICS_UNQUOTED_IDENTIFIER", - "CLOSING_METRICS_BY", "CLOSING_METRICS_PIPE", + "SETTTING_MULTILINE_COMMENT", "SETTING_WS", "METRICS_PIPE", "METRICS_UNQUOTED_SOURCE", + "METRICS_QUOTED_SOURCE", "METRICS_LINE_COMMENT", "METRICS_MULTILINE_COMMENT", + "METRICS_WS", "CLOSING_METRICS_COLON", "CLOSING_METRICS_COMMA", "CLOSING_METRICS_LINE_COMMENT", + "CLOSING_METRICS_MULTILINE_COMMENT", "CLOSING_METRICS_WS", "CLOSING_METRICS_QUOTED_IDENTIFIER", + "CLOSING_METRICS_UNQUOTED_IDENTIFIER", "CLOSING_METRICS_BY", "CLOSING_METRICS_PIPE", ]; @@ -378,7 +379,7 @@ export default class esql_lexer extends Lexer { public get modeNames(): string[] { return esql_lexer.modeNames; } - public static readonly _serializedATN: number[] = [4,0,124,1422,6,-1,6, + public static readonly _serializedATN: number[] = [4,0,124,1450,6,-1,6, -1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1, 2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8, 2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16, @@ -408,463 +409,474 @@ export default class esql_lexer extends Lexer { 2,169,7,169,2,170,7,170,2,171,7,171,2,172,7,172,2,173,7,173,2,174,7,174, 2,175,7,175,2,176,7,176,2,177,7,177,2,178,7,178,2,179,7,179,2,180,7,180, 2,181,7,181,2,182,7,182,2,183,7,183,2,184,7,184,2,185,7,185,2,186,7,186, - 2,187,7,187,2,188,7,188,2,189,7,189,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3, - 1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5, - 1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7, - 1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9, - 1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1, - 11,1,11,1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12, - 1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1, - 14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,16, - 1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1, - 18,1,18,1,18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19, - 1,20,4,20,567,8,20,11,20,12,20,568,1,20,1,20,1,21,1,21,1,21,1,21,5,21,577, - 8,21,10,21,12,21,580,9,21,1,21,3,21,583,8,21,1,21,3,21,586,8,21,1,21,1, - 21,1,22,1,22,1,22,1,22,1,22,5,22,595,8,22,10,22,12,22,598,9,22,1,22,1,22, - 1,22,1,22,1,22,1,23,4,23,606,8,23,11,23,12,23,607,1,23,1,23,1,24,1,24,1, - 24,3,24,615,8,24,1,25,4,25,618,8,25,11,25,12,25,619,1,26,1,26,1,26,1,26, - 1,26,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,29,1,29,1,29,1,29,1, - 30,1,30,1,30,1,30,1,31,1,31,1,31,1,31,1,32,1,32,1,33,1,33,1,34,1,34,1,34, - 1,35,1,35,1,36,1,36,3,36,659,8,36,1,36,4,36,662,8,36,11,36,12,36,663,1, - 37,1,37,1,38,1,38,1,39,1,39,1,39,3,39,673,8,39,1,40,1,40,1,41,1,41,1,41, - 3,41,680,8,41,1,42,1,42,1,42,5,42,685,8,42,10,42,12,42,688,9,42,1,42,1, - 42,1,42,1,42,1,42,1,42,5,42,696,8,42,10,42,12,42,699,9,42,1,42,1,42,1,42, - 1,42,1,42,3,42,706,8,42,1,42,3,42,709,8,42,3,42,711,8,42,1,43,4,43,714, - 8,43,11,43,12,43,715,1,44,4,44,719,8,44,11,44,12,44,720,1,44,1,44,5,44, - 725,8,44,10,44,12,44,728,9,44,1,44,1,44,4,44,732,8,44,11,44,12,44,733,1, - 44,4,44,737,8,44,11,44,12,44,738,1,44,1,44,5,44,743,8,44,10,44,12,44,746, - 9,44,3,44,748,8,44,1,44,1,44,1,44,1,44,4,44,754,8,44,11,44,12,44,755,1, - 44,1,44,3,44,760,8,44,1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,47,1,47,1,47, - 1,47,1,48,1,48,1,49,1,49,1,49,1,50,1,50,1,51,1,51,1,51,1,51,1,51,1,52,1, - 52,1,53,1,53,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,55,1,55, - 1,55,1,55,1,55,1,56,1,56,1,57,1,57,1,57,1,58,1,58,1,58,1,59,1,59,1,59,1, - 59,1,59,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,62,1,62,1,62,1,62, - 1,62,1,62,1,63,1,63,1,63,1,64,1,64,1,65,1,65,1,65,1,65,1,65,1,65,1,66,1, - 66,1,67,1,67,1,67,1,67,1,67,1,68,1,68,1,68,1,69,1,69,1,69,1,70,1,70,1,70, - 1,71,1,71,1,72,1,72,1,72,1,73,1,73,1,74,1,74,1,74,1,75,1,75,1,76,1,76,1, - 77,1,77,1,78,1,78,1,79,1,79,1,80,1,80,1,80,5,80,882,8,80,10,80,12,80,885, - 9,80,1,80,1,80,4,80,889,8,80,11,80,12,80,890,3,80,893,8,80,1,81,1,81,1, - 81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,1,83,1,83,5,83,907,8,83,10,83,12, - 83,910,9,83,1,83,1,83,3,83,914,8,83,1,83,4,83,917,8,83,11,83,12,83,918, - 3,83,921,8,83,1,84,1,84,4,84,925,8,84,11,84,12,84,926,1,84,1,84,1,85,1, - 85,1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,89,1,89, - 1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,91,1,91,1,91,1,91,1,92,1,92,1,92,1, - 92,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1,95,1,95,1,95,1,95,1,95,1,95, - 1,95,1,95,1,95,1,96,1,96,1,96,1,96,1,97,1,97,1,97,1,97,1,98,1,98,1,98,1, - 98,1,99,1,99,1,99,1,99,1,100,1,100,1,100,1,100,1,100,1,101,1,101,1,101, - 1,101,1,102,1,102,1,102,1,102,1,103,1,103,1,103,1,103,3,103,1012,8,103, - 1,104,1,104,3,104,1016,8,104,1,104,5,104,1019,8,104,10,104,12,104,1022, - 9,104,1,104,1,104,3,104,1026,8,104,1,104,4,104,1029,8,104,11,104,12,104, - 1030,3,104,1033,8,104,1,105,1,105,4,105,1037,8,105,11,105,12,105,1038,1, - 106,1,106,1,106,1,106,1,107,1,107,1,107,1,107,1,108,1,108,1,108,1,108,1, - 109,1,109,1,109,1,109,1,109,1,110,1,110,1,110,1,110,1,111,1,111,1,111,1, - 111,1,112,1,112,1,112,1,112,1,113,1,113,1,113,1,114,1,114,1,114,1,114,1, - 115,1,115,1,115,1,115,1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117,1, - 118,1,118,1,118,1,118,1,118,1,119,1,119,1,119,1,119,1,119,1,120,1,120,1, - 120,1,120,1,120,1,121,1,121,1,121,1,121,1,121,1,121,1,121,1,122,1,122,1, - 123,4,123,1114,8,123,11,123,12,123,1115,1,123,1,123,3,123,1120,8,123,1, - 123,4,123,1123,8,123,11,123,12,123,1124,1,124,1,124,1,124,1,124,1,125,1, - 125,1,125,1,125,1,126,1,126,1,126,1,126,1,127,1,127,1,127,1,127,1,128,1, - 128,1,128,1,128,1,129,1,129,1,129,1,129,1,129,1,129,1,130,1,130,1,130,1, - 130,1,131,1,131,1,131,1,131,1,132,1,132,1,132,1,132,1,133,1,133,1,133,1, - 133,1,134,1,134,1,134,1,134,1,135,1,135,1,135,1,135,1,136,1,136,1,136,1, - 136,1,137,1,137,1,137,1,137,1,138,1,138,1,138,1,138,1,139,1,139,1,139,1, - 139,1,139,1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,142,1,142,1, - 142,1,142,1,142,1,143,1,143,1,143,1,143,1,144,1,144,1,144,1,144,1,145,1, - 145,1,145,1,145,1,146,1,146,1,146,1,146,1,147,1,147,1,147,1,147,1,147,1, - 147,1,148,1,148,1,148,1,148,1,149,1,149,1,149,1,149,1,150,1,150,1,150,1, - 150,1,151,1,151,1,151,1,151,1,152,1,152,1,152,1,152,1,153,1,153,1,153,1, - 153,1,154,1,154,1,154,1,154,1,154,1,155,1,155,1,155,1,155,1,156,1,156,1, - 156,1,156,1,157,1,157,1,157,1,157,1,158,1,158,1,158,1,158,1,159,1,159,1, - 159,1,159,1,160,1,160,1,160,1,160,1,161,1,161,1,161,1,161,1,161,1,162,1, - 162,1,162,1,162,1,162,1,163,1,163,1,163,1,163,1,164,1,164,1,164,1,164,1, - 165,1,165,1,165,1,165,1,166,1,166,1,166,1,166,1,166,1,167,1,167,1,167,1, - 167,1,167,1,167,1,167,1,167,1,167,1,167,1,168,1,168,1,168,1,168,1,169,1, - 169,1,169,1,169,1,170,1,170,1,170,1,170,1,171,1,171,1,171,1,171,1,171,1, - 172,1,172,1,173,1,173,1,173,1,173,1,173,4,173,1343,8,173,11,173,12,173, - 1344,1,174,1,174,1,174,1,174,1,175,1,175,1,175,1,175,1,176,1,176,1,176, - 1,176,1,177,1,177,1,177,1,177,1,177,1,178,1,178,1,178,1,178,1,178,1,178, - 1,179,1,179,1,179,1,179,1,180,1,180,1,180,1,180,1,181,1,181,1,181,1,181, - 1,182,1,182,1,182,1,182,1,182,1,182,1,183,1,183,1,183,1,183,1,184,1,184, - 1,184,1,184,1,185,1,185,1,185,1,185,1,186,1,186,1,186,1,186,1,186,1,186, - 1,187,1,187,1,187,1,187,1,187,1,187,1,188,1,188,1,188,1,188,1,188,1,188, - 1,189,1,189,1,189,1,189,1,189,2,596,697,0,190,16,1,18,2,20,3,22,4,24,5, - 26,6,28,7,30,8,32,9,34,10,36,11,38,12,40,13,42,14,44,15,46,16,48,17,50, - 18,52,19,54,20,56,21,58,22,60,23,62,24,64,0,66,25,68,0,70,0,72,26,74,27, - 76,28,78,29,80,0,82,0,84,0,86,0,88,0,90,0,92,0,94,0,96,0,98,0,100,30,102, - 31,104,32,106,33,108,34,110,35,112,36,114,37,116,38,118,39,120,40,122,41, - 124,42,126,43,128,44,130,45,132,46,134,47,136,48,138,49,140,50,142,51,144, - 52,146,53,148,54,150,55,152,56,154,57,156,58,158,59,160,60,162,61,164,62, - 166,63,168,64,170,65,172,66,174,67,176,68,178,69,180,70,182,71,184,0,186, - 72,188,73,190,74,192,75,194,0,196,0,198,0,200,0,202,0,204,0,206,76,208, - 0,210,77,212,78,214,79,216,0,218,0,220,0,222,0,224,0,226,80,228,81,230, - 82,232,83,234,0,236,0,238,0,240,0,242,84,244,0,246,85,248,86,250,87,252, - 0,254,0,256,88,258,89,260,0,262,90,264,0,266,0,268,91,270,92,272,93,274, - 0,276,0,278,0,280,0,282,0,284,0,286,0,288,94,290,95,292,96,294,0,296,0, - 298,0,300,0,302,0,304,97,306,98,308,99,310,0,312,0,314,0,316,0,318,100, - 320,101,322,102,324,0,326,0,328,0,330,0,332,103,334,104,336,105,338,0,340, - 106,342,107,344,108,346,109,348,0,350,110,352,111,354,112,356,113,358,0, - 360,114,362,115,364,116,366,117,368,118,370,0,372,0,374,119,376,120,378, - 121,380,0,382,122,384,123,386,124,388,0,390,0,392,0,394,0,16,0,1,2,3,4, - 5,6,7,8,9,10,11,12,13,14,15,35,2,0,68,68,100,100,2,0,73,73,105,105,2,0, - 83,83,115,115,2,0,69,69,101,101,2,0,67,67,99,99,2,0,84,84,116,116,2,0,82, - 82,114,114,2,0,79,79,111,111,2,0,80,80,112,112,2,0,78,78,110,110,2,0,72, - 72,104,104,2,0,86,86,118,118,2,0,65,65,97,97,2,0,76,76,108,108,2,0,88,88, - 120,120,2,0,70,70,102,102,2,0,77,77,109,109,2,0,71,71,103,103,2,0,75,75, - 107,107,2,0,85,85,117,117,2,0,87,87,119,119,6,0,9,10,13,13,32,32,47,47, - 91,91,93,93,2,0,10,10,13,13,3,0,9,10,13,13,32,32,10,0,9,10,13,13,32,32, - 44,44,47,47,61,61,91,91,93,93,96,96,124,124,2,0,42,42,47,47,1,0,48,57,2, - 0,65,90,97,122,8,0,34,34,78,78,82,82,84,84,92,92,110,110,114,114,116,116, - 4,0,10,10,13,13,34,34,92,92,2,0,43,43,45,45,1,0,96,96,2,0,66,66,98,98,2, - 0,89,89,121,121,11,0,9,10,13,13,32,32,34,35,44,44,47,47,58,58,60,60,62, - 63,92,92,124,124,1448,0,16,1,0,0,0,0,18,1,0,0,0,0,20,1,0,0,0,0,22,1,0,0, - 0,0,24,1,0,0,0,0,26,1,0,0,0,0,28,1,0,0,0,0,30,1,0,0,0,0,32,1,0,0,0,0,34, - 1,0,0,0,0,36,1,0,0,0,0,38,1,0,0,0,0,40,1,0,0,0,0,42,1,0,0,0,0,44,1,0,0, - 0,0,46,1,0,0,0,0,48,1,0,0,0,0,50,1,0,0,0,0,52,1,0,0,0,0,54,1,0,0,0,0,56, - 1,0,0,0,0,58,1,0,0,0,0,60,1,0,0,0,0,62,1,0,0,0,0,66,1,0,0,0,1,68,1,0,0, - 0,1,70,1,0,0,0,1,72,1,0,0,0,1,74,1,0,0,0,1,76,1,0,0,0,2,78,1,0,0,0,2,100, - 1,0,0,0,2,102,1,0,0,0,2,104,1,0,0,0,2,106,1,0,0,0,2,108,1,0,0,0,2,110,1, - 0,0,0,2,112,1,0,0,0,2,114,1,0,0,0,2,116,1,0,0,0,2,118,1,0,0,0,2,120,1,0, - 0,0,2,122,1,0,0,0,2,124,1,0,0,0,2,126,1,0,0,0,2,128,1,0,0,0,2,130,1,0,0, - 0,2,132,1,0,0,0,2,134,1,0,0,0,2,136,1,0,0,0,2,138,1,0,0,0,2,140,1,0,0,0, - 2,142,1,0,0,0,2,144,1,0,0,0,2,146,1,0,0,0,2,148,1,0,0,0,2,150,1,0,0,0,2, - 152,1,0,0,0,2,154,1,0,0,0,2,156,1,0,0,0,2,158,1,0,0,0,2,160,1,0,0,0,2,162, - 1,0,0,0,2,164,1,0,0,0,2,166,1,0,0,0,2,168,1,0,0,0,2,170,1,0,0,0,2,172,1, - 0,0,0,2,174,1,0,0,0,2,176,1,0,0,0,2,178,1,0,0,0,2,180,1,0,0,0,2,182,1,0, - 0,0,2,186,1,0,0,0,2,188,1,0,0,0,2,190,1,0,0,0,2,192,1,0,0,0,3,194,1,0,0, - 0,3,196,1,0,0,0,3,198,1,0,0,0,3,200,1,0,0,0,3,202,1,0,0,0,3,204,1,0,0,0, - 3,206,1,0,0,0,3,208,1,0,0,0,3,210,1,0,0,0,3,212,1,0,0,0,3,214,1,0,0,0,4, - 216,1,0,0,0,4,218,1,0,0,0,4,220,1,0,0,0,4,226,1,0,0,0,4,228,1,0,0,0,4,230, - 1,0,0,0,4,232,1,0,0,0,5,234,1,0,0,0,5,236,1,0,0,0,5,238,1,0,0,0,5,240,1, - 0,0,0,5,242,1,0,0,0,5,244,1,0,0,0,5,246,1,0,0,0,5,248,1,0,0,0,5,250,1,0, - 0,0,6,252,1,0,0,0,6,254,1,0,0,0,6,256,1,0,0,0,6,258,1,0,0,0,6,262,1,0,0, - 0,6,264,1,0,0,0,6,266,1,0,0,0,6,268,1,0,0,0,6,270,1,0,0,0,6,272,1,0,0,0, - 7,274,1,0,0,0,7,276,1,0,0,0,7,278,1,0,0,0,7,280,1,0,0,0,7,282,1,0,0,0,7, - 284,1,0,0,0,7,286,1,0,0,0,7,288,1,0,0,0,7,290,1,0,0,0,7,292,1,0,0,0,8,294, - 1,0,0,0,8,296,1,0,0,0,8,298,1,0,0,0,8,300,1,0,0,0,8,302,1,0,0,0,8,304,1, - 0,0,0,8,306,1,0,0,0,8,308,1,0,0,0,9,310,1,0,0,0,9,312,1,0,0,0,9,314,1,0, - 0,0,9,316,1,0,0,0,9,318,1,0,0,0,9,320,1,0,0,0,9,322,1,0,0,0,10,324,1,0, - 0,0,10,326,1,0,0,0,10,328,1,0,0,0,10,330,1,0,0,0,10,332,1,0,0,0,10,334, - 1,0,0,0,10,336,1,0,0,0,11,338,1,0,0,0,11,340,1,0,0,0,11,342,1,0,0,0,11, - 344,1,0,0,0,11,346,1,0,0,0,12,348,1,0,0,0,12,350,1,0,0,0,12,352,1,0,0,0, - 12,354,1,0,0,0,12,356,1,0,0,0,13,358,1,0,0,0,13,360,1,0,0,0,13,362,1,0, - 0,0,13,364,1,0,0,0,13,366,1,0,0,0,13,368,1,0,0,0,14,370,1,0,0,0,14,372, - 1,0,0,0,14,374,1,0,0,0,14,376,1,0,0,0,14,378,1,0,0,0,15,380,1,0,0,0,15, - 382,1,0,0,0,15,384,1,0,0,0,15,386,1,0,0,0,15,388,1,0,0,0,15,390,1,0,0,0, - 15,392,1,0,0,0,15,394,1,0,0,0,16,396,1,0,0,0,18,406,1,0,0,0,20,413,1,0, - 0,0,22,422,1,0,0,0,24,429,1,0,0,0,26,439,1,0,0,0,28,446,1,0,0,0,30,453, - 1,0,0,0,32,467,1,0,0,0,34,474,1,0,0,0,36,482,1,0,0,0,38,491,1,0,0,0,40, - 498,1,0,0,0,42,508,1,0,0,0,44,520,1,0,0,0,46,529,1,0,0,0,48,535,1,0,0,0, - 50,542,1,0,0,0,52,549,1,0,0,0,54,557,1,0,0,0,56,566,1,0,0,0,58,572,1,0, - 0,0,60,589,1,0,0,0,62,605,1,0,0,0,64,614,1,0,0,0,66,617,1,0,0,0,68,621, - 1,0,0,0,70,626,1,0,0,0,72,631,1,0,0,0,74,635,1,0,0,0,76,639,1,0,0,0,78, - 643,1,0,0,0,80,647,1,0,0,0,82,649,1,0,0,0,84,651,1,0,0,0,86,654,1,0,0,0, - 88,656,1,0,0,0,90,665,1,0,0,0,92,667,1,0,0,0,94,672,1,0,0,0,96,674,1,0, - 0,0,98,679,1,0,0,0,100,710,1,0,0,0,102,713,1,0,0,0,104,759,1,0,0,0,106, - 761,1,0,0,0,108,764,1,0,0,0,110,768,1,0,0,0,112,772,1,0,0,0,114,774,1,0, - 0,0,116,777,1,0,0,0,118,779,1,0,0,0,120,784,1,0,0,0,122,786,1,0,0,0,124, - 792,1,0,0,0,126,798,1,0,0,0,128,803,1,0,0,0,130,805,1,0,0,0,132,808,1,0, - 0,0,134,811,1,0,0,0,136,816,1,0,0,0,138,820,1,0,0,0,140,825,1,0,0,0,142, - 831,1,0,0,0,144,834,1,0,0,0,146,836,1,0,0,0,148,842,1,0,0,0,150,844,1,0, - 0,0,152,849,1,0,0,0,154,852,1,0,0,0,156,855,1,0,0,0,158,858,1,0,0,0,160, - 860,1,0,0,0,162,863,1,0,0,0,164,865,1,0,0,0,166,868,1,0,0,0,168,870,1,0, - 0,0,170,872,1,0,0,0,172,874,1,0,0,0,174,876,1,0,0,0,176,892,1,0,0,0,178, - 894,1,0,0,0,180,899,1,0,0,0,182,920,1,0,0,0,184,922,1,0,0,0,186,930,1,0, - 0,0,188,932,1,0,0,0,190,936,1,0,0,0,192,940,1,0,0,0,194,944,1,0,0,0,196, - 949,1,0,0,0,198,953,1,0,0,0,200,957,1,0,0,0,202,961,1,0,0,0,204,965,1,0, - 0,0,206,969,1,0,0,0,208,978,1,0,0,0,210,982,1,0,0,0,212,986,1,0,0,0,214, - 990,1,0,0,0,216,994,1,0,0,0,218,999,1,0,0,0,220,1003,1,0,0,0,222,1011,1, - 0,0,0,224,1032,1,0,0,0,226,1036,1,0,0,0,228,1040,1,0,0,0,230,1044,1,0,0, - 0,232,1048,1,0,0,0,234,1052,1,0,0,0,236,1057,1,0,0,0,238,1061,1,0,0,0,240, - 1065,1,0,0,0,242,1069,1,0,0,0,244,1072,1,0,0,0,246,1076,1,0,0,0,248,1080, - 1,0,0,0,250,1084,1,0,0,0,252,1088,1,0,0,0,254,1093,1,0,0,0,256,1098,1,0, - 0,0,258,1103,1,0,0,0,260,1110,1,0,0,0,262,1119,1,0,0,0,264,1126,1,0,0,0, - 266,1130,1,0,0,0,268,1134,1,0,0,0,270,1138,1,0,0,0,272,1142,1,0,0,0,274, - 1146,1,0,0,0,276,1152,1,0,0,0,278,1156,1,0,0,0,280,1160,1,0,0,0,282,1164, - 1,0,0,0,284,1168,1,0,0,0,286,1172,1,0,0,0,288,1176,1,0,0,0,290,1180,1,0, - 0,0,292,1184,1,0,0,0,294,1188,1,0,0,0,296,1193,1,0,0,0,298,1197,1,0,0,0, - 300,1201,1,0,0,0,302,1206,1,0,0,0,304,1210,1,0,0,0,306,1214,1,0,0,0,308, - 1218,1,0,0,0,310,1222,1,0,0,0,312,1228,1,0,0,0,314,1232,1,0,0,0,316,1236, - 1,0,0,0,318,1240,1,0,0,0,320,1244,1,0,0,0,322,1248,1,0,0,0,324,1252,1,0, - 0,0,326,1257,1,0,0,0,328,1261,1,0,0,0,330,1265,1,0,0,0,332,1269,1,0,0,0, - 334,1273,1,0,0,0,336,1277,1,0,0,0,338,1281,1,0,0,0,340,1286,1,0,0,0,342, - 1291,1,0,0,0,344,1295,1,0,0,0,346,1299,1,0,0,0,348,1303,1,0,0,0,350,1308, - 1,0,0,0,352,1318,1,0,0,0,354,1322,1,0,0,0,356,1326,1,0,0,0,358,1330,1,0, - 0,0,360,1335,1,0,0,0,362,1342,1,0,0,0,364,1346,1,0,0,0,366,1350,1,0,0,0, - 368,1354,1,0,0,0,370,1358,1,0,0,0,372,1363,1,0,0,0,374,1369,1,0,0,0,376, - 1373,1,0,0,0,378,1377,1,0,0,0,380,1381,1,0,0,0,382,1387,1,0,0,0,384,1391, - 1,0,0,0,386,1395,1,0,0,0,388,1399,1,0,0,0,390,1405,1,0,0,0,392,1411,1,0, - 0,0,394,1417,1,0,0,0,396,397,7,0,0,0,397,398,7,1,0,0,398,399,7,2,0,0,399, - 400,7,2,0,0,400,401,7,3,0,0,401,402,7,4,0,0,402,403,7,5,0,0,403,404,1,0, - 0,0,404,405,6,0,0,0,405,17,1,0,0,0,406,407,7,0,0,0,407,408,7,6,0,0,408, - 409,7,7,0,0,409,410,7,8,0,0,410,411,1,0,0,0,411,412,6,1,1,0,412,19,1,0, - 0,0,413,414,7,3,0,0,414,415,7,9,0,0,415,416,7,6,0,0,416,417,7,1,0,0,417, - 418,7,4,0,0,418,419,7,10,0,0,419,420,1,0,0,0,420,421,6,2,2,0,421,21,1,0, - 0,0,422,423,7,3,0,0,423,424,7,11,0,0,424,425,7,12,0,0,425,426,7,13,0,0, - 426,427,1,0,0,0,427,428,6,3,0,0,428,23,1,0,0,0,429,430,7,3,0,0,430,431, - 7,14,0,0,431,432,7,8,0,0,432,433,7,13,0,0,433,434,7,12,0,0,434,435,7,1, - 0,0,435,436,7,9,0,0,436,437,1,0,0,0,437,438,6,4,3,0,438,25,1,0,0,0,439, - 440,7,15,0,0,440,441,7,6,0,0,441,442,7,7,0,0,442,443,7,16,0,0,443,444,1, - 0,0,0,444,445,6,5,4,0,445,27,1,0,0,0,446,447,7,17,0,0,447,448,7,6,0,0,448, - 449,7,7,0,0,449,450,7,18,0,0,450,451,1,0,0,0,451,452,6,6,0,0,452,29,1,0, - 0,0,453,454,7,1,0,0,454,455,7,9,0,0,455,456,7,13,0,0,456,457,7,1,0,0,457, - 458,7,9,0,0,458,459,7,3,0,0,459,460,7,2,0,0,460,461,7,5,0,0,461,462,7,12, - 0,0,462,463,7,5,0,0,463,464,7,2,0,0,464,465,1,0,0,0,465,466,6,7,0,0,466, - 31,1,0,0,0,467,468,7,18,0,0,468,469,7,3,0,0,469,470,7,3,0,0,470,471,7,8, - 0,0,471,472,1,0,0,0,472,473,6,8,1,0,473,33,1,0,0,0,474,475,7,13,0,0,475, - 476,7,1,0,0,476,477,7,16,0,0,477,478,7,1,0,0,478,479,7,5,0,0,479,480,1, - 0,0,0,480,481,6,9,0,0,481,35,1,0,0,0,482,483,7,13,0,0,483,484,7,7,0,0,484, - 485,7,7,0,0,485,486,7,18,0,0,486,487,7,19,0,0,487,488,7,8,0,0,488,489,1, - 0,0,0,489,490,6,10,5,0,490,37,1,0,0,0,491,492,7,16,0,0,492,493,7,3,0,0, - 493,494,7,5,0,0,494,495,7,12,0,0,495,496,1,0,0,0,496,497,6,11,6,0,497,39, - 1,0,0,0,498,499,7,16,0,0,499,500,7,3,0,0,500,501,7,5,0,0,501,502,7,6,0, - 0,502,503,7,1,0,0,503,504,7,4,0,0,504,505,7,2,0,0,505,506,1,0,0,0,506,507, - 6,12,7,0,507,41,1,0,0,0,508,509,7,16,0,0,509,510,7,11,0,0,510,511,5,95, - 0,0,511,512,7,3,0,0,512,513,7,14,0,0,513,514,7,8,0,0,514,515,7,12,0,0,515, - 516,7,9,0,0,516,517,7,0,0,0,517,518,1,0,0,0,518,519,6,13,8,0,519,43,1,0, - 0,0,520,521,7,6,0,0,521,522,7,3,0,0,522,523,7,9,0,0,523,524,7,12,0,0,524, - 525,7,16,0,0,525,526,7,3,0,0,526,527,1,0,0,0,527,528,6,14,9,0,528,45,1, - 0,0,0,529,530,7,6,0,0,530,531,7,7,0,0,531,532,7,20,0,0,532,533,1,0,0,0, - 533,534,6,15,0,0,534,47,1,0,0,0,535,536,7,2,0,0,536,537,7,10,0,0,537,538, - 7,7,0,0,538,539,7,20,0,0,539,540,1,0,0,0,540,541,6,16,10,0,541,49,1,0,0, - 0,542,543,7,2,0,0,543,544,7,7,0,0,544,545,7,6,0,0,545,546,7,5,0,0,546,547, - 1,0,0,0,547,548,6,17,0,0,548,51,1,0,0,0,549,550,7,2,0,0,550,551,7,5,0,0, - 551,552,7,12,0,0,552,553,7,5,0,0,553,554,7,2,0,0,554,555,1,0,0,0,555,556, - 6,18,0,0,556,53,1,0,0,0,557,558,7,20,0,0,558,559,7,10,0,0,559,560,7,3,0, - 0,560,561,7,6,0,0,561,562,7,3,0,0,562,563,1,0,0,0,563,564,6,19,0,0,564, - 55,1,0,0,0,565,567,8,21,0,0,566,565,1,0,0,0,567,568,1,0,0,0,568,566,1,0, - 0,0,568,569,1,0,0,0,569,570,1,0,0,0,570,571,6,20,0,0,571,57,1,0,0,0,572, - 573,5,47,0,0,573,574,5,47,0,0,574,578,1,0,0,0,575,577,8,22,0,0,576,575, - 1,0,0,0,577,580,1,0,0,0,578,576,1,0,0,0,578,579,1,0,0,0,579,582,1,0,0,0, - 580,578,1,0,0,0,581,583,5,13,0,0,582,581,1,0,0,0,582,583,1,0,0,0,583,585, - 1,0,0,0,584,586,5,10,0,0,585,584,1,0,0,0,585,586,1,0,0,0,586,587,1,0,0, - 0,587,588,6,21,11,0,588,59,1,0,0,0,589,590,5,47,0,0,590,591,5,42,0,0,591, - 596,1,0,0,0,592,595,3,60,22,0,593,595,9,0,0,0,594,592,1,0,0,0,594,593,1, - 0,0,0,595,598,1,0,0,0,596,597,1,0,0,0,596,594,1,0,0,0,597,599,1,0,0,0,598, - 596,1,0,0,0,599,600,5,42,0,0,600,601,5,47,0,0,601,602,1,0,0,0,602,603,6, - 22,11,0,603,61,1,0,0,0,604,606,7,23,0,0,605,604,1,0,0,0,606,607,1,0,0,0, - 607,605,1,0,0,0,607,608,1,0,0,0,608,609,1,0,0,0,609,610,6,23,11,0,610,63, - 1,0,0,0,611,615,8,24,0,0,612,613,5,47,0,0,613,615,8,25,0,0,614,611,1,0, - 0,0,614,612,1,0,0,0,615,65,1,0,0,0,616,618,3,64,24,0,617,616,1,0,0,0,618, - 619,1,0,0,0,619,617,1,0,0,0,619,620,1,0,0,0,620,67,1,0,0,0,621,622,3,178, - 81,0,622,623,1,0,0,0,623,624,6,26,12,0,624,625,6,26,13,0,625,69,1,0,0,0, - 626,627,3,78,31,0,627,628,1,0,0,0,628,629,6,27,14,0,629,630,6,27,15,0,630, - 71,1,0,0,0,631,632,3,62,23,0,632,633,1,0,0,0,633,634,6,28,11,0,634,73,1, - 0,0,0,635,636,3,58,21,0,636,637,1,0,0,0,637,638,6,29,11,0,638,75,1,0,0, - 0,639,640,3,60,22,0,640,641,1,0,0,0,641,642,6,30,11,0,642,77,1,0,0,0,643, - 644,5,124,0,0,644,645,1,0,0,0,645,646,6,31,15,0,646,79,1,0,0,0,647,648, - 7,26,0,0,648,81,1,0,0,0,649,650,7,27,0,0,650,83,1,0,0,0,651,652,5,92,0, - 0,652,653,7,28,0,0,653,85,1,0,0,0,654,655,8,29,0,0,655,87,1,0,0,0,656,658, - 7,3,0,0,657,659,7,30,0,0,658,657,1,0,0,0,658,659,1,0,0,0,659,661,1,0,0, - 0,660,662,3,80,32,0,661,660,1,0,0,0,662,663,1,0,0,0,663,661,1,0,0,0,663, - 664,1,0,0,0,664,89,1,0,0,0,665,666,5,64,0,0,666,91,1,0,0,0,667,668,5,96, - 0,0,668,93,1,0,0,0,669,673,8,31,0,0,670,671,5,96,0,0,671,673,5,96,0,0,672, - 669,1,0,0,0,672,670,1,0,0,0,673,95,1,0,0,0,674,675,5,95,0,0,675,97,1,0, - 0,0,676,680,3,82,33,0,677,680,3,80,32,0,678,680,3,96,40,0,679,676,1,0,0, - 0,679,677,1,0,0,0,679,678,1,0,0,0,680,99,1,0,0,0,681,686,5,34,0,0,682,685, - 3,84,34,0,683,685,3,86,35,0,684,682,1,0,0,0,684,683,1,0,0,0,685,688,1,0, - 0,0,686,684,1,0,0,0,686,687,1,0,0,0,687,689,1,0,0,0,688,686,1,0,0,0,689, - 711,5,34,0,0,690,691,5,34,0,0,691,692,5,34,0,0,692,693,5,34,0,0,693,697, - 1,0,0,0,694,696,8,22,0,0,695,694,1,0,0,0,696,699,1,0,0,0,697,698,1,0,0, - 0,697,695,1,0,0,0,698,700,1,0,0,0,699,697,1,0,0,0,700,701,5,34,0,0,701, - 702,5,34,0,0,702,703,5,34,0,0,703,705,1,0,0,0,704,706,5,34,0,0,705,704, - 1,0,0,0,705,706,1,0,0,0,706,708,1,0,0,0,707,709,5,34,0,0,708,707,1,0,0, - 0,708,709,1,0,0,0,709,711,1,0,0,0,710,681,1,0,0,0,710,690,1,0,0,0,711,101, - 1,0,0,0,712,714,3,80,32,0,713,712,1,0,0,0,714,715,1,0,0,0,715,713,1,0,0, - 0,715,716,1,0,0,0,716,103,1,0,0,0,717,719,3,80,32,0,718,717,1,0,0,0,719, - 720,1,0,0,0,720,718,1,0,0,0,720,721,1,0,0,0,721,722,1,0,0,0,722,726,3,120, - 52,0,723,725,3,80,32,0,724,723,1,0,0,0,725,728,1,0,0,0,726,724,1,0,0,0, - 726,727,1,0,0,0,727,760,1,0,0,0,728,726,1,0,0,0,729,731,3,120,52,0,730, - 732,3,80,32,0,731,730,1,0,0,0,732,733,1,0,0,0,733,731,1,0,0,0,733,734,1, - 0,0,0,734,760,1,0,0,0,735,737,3,80,32,0,736,735,1,0,0,0,737,738,1,0,0,0, - 738,736,1,0,0,0,738,739,1,0,0,0,739,747,1,0,0,0,740,744,3,120,52,0,741, - 743,3,80,32,0,742,741,1,0,0,0,743,746,1,0,0,0,744,742,1,0,0,0,744,745,1, - 0,0,0,745,748,1,0,0,0,746,744,1,0,0,0,747,740,1,0,0,0,747,748,1,0,0,0,748, - 749,1,0,0,0,749,750,3,88,36,0,750,760,1,0,0,0,751,753,3,120,52,0,752,754, - 3,80,32,0,753,752,1,0,0,0,754,755,1,0,0,0,755,753,1,0,0,0,755,756,1,0,0, - 0,756,757,1,0,0,0,757,758,3,88,36,0,758,760,1,0,0,0,759,718,1,0,0,0,759, - 729,1,0,0,0,759,736,1,0,0,0,759,751,1,0,0,0,760,105,1,0,0,0,761,762,7,32, - 0,0,762,763,7,33,0,0,763,107,1,0,0,0,764,765,7,12,0,0,765,766,7,9,0,0,766, - 767,7,0,0,0,767,109,1,0,0,0,768,769,7,12,0,0,769,770,7,2,0,0,770,771,7, - 4,0,0,771,111,1,0,0,0,772,773,5,61,0,0,773,113,1,0,0,0,774,775,5,58,0,0, - 775,776,5,58,0,0,776,115,1,0,0,0,777,778,5,44,0,0,778,117,1,0,0,0,779,780, - 7,0,0,0,780,781,7,3,0,0,781,782,7,2,0,0,782,783,7,4,0,0,783,119,1,0,0,0, - 784,785,5,46,0,0,785,121,1,0,0,0,786,787,7,15,0,0,787,788,7,12,0,0,788, - 789,7,13,0,0,789,790,7,2,0,0,790,791,7,3,0,0,791,123,1,0,0,0,792,793,7, - 15,0,0,793,794,7,1,0,0,794,795,7,6,0,0,795,796,7,2,0,0,796,797,7,5,0,0, - 797,125,1,0,0,0,798,799,7,13,0,0,799,800,7,12,0,0,800,801,7,2,0,0,801,802, - 7,5,0,0,802,127,1,0,0,0,803,804,5,40,0,0,804,129,1,0,0,0,805,806,7,1,0, - 0,806,807,7,9,0,0,807,131,1,0,0,0,808,809,7,1,0,0,809,810,7,2,0,0,810,133, - 1,0,0,0,811,812,7,13,0,0,812,813,7,1,0,0,813,814,7,18,0,0,814,815,7,3,0, - 0,815,135,1,0,0,0,816,817,7,9,0,0,817,818,7,7,0,0,818,819,7,5,0,0,819,137, - 1,0,0,0,820,821,7,9,0,0,821,822,7,19,0,0,822,823,7,13,0,0,823,824,7,13, - 0,0,824,139,1,0,0,0,825,826,7,9,0,0,826,827,7,19,0,0,827,828,7,13,0,0,828, - 829,7,13,0,0,829,830,7,2,0,0,830,141,1,0,0,0,831,832,7,7,0,0,832,833,7, - 6,0,0,833,143,1,0,0,0,834,835,5,63,0,0,835,145,1,0,0,0,836,837,7,6,0,0, - 837,838,7,13,0,0,838,839,7,1,0,0,839,840,7,18,0,0,840,841,7,3,0,0,841,147, - 1,0,0,0,842,843,5,41,0,0,843,149,1,0,0,0,844,845,7,5,0,0,845,846,7,6,0, - 0,846,847,7,19,0,0,847,848,7,3,0,0,848,151,1,0,0,0,849,850,5,61,0,0,850, - 851,5,61,0,0,851,153,1,0,0,0,852,853,5,61,0,0,853,854,5,126,0,0,854,155, - 1,0,0,0,855,856,5,33,0,0,856,857,5,61,0,0,857,157,1,0,0,0,858,859,5,60, - 0,0,859,159,1,0,0,0,860,861,5,60,0,0,861,862,5,61,0,0,862,161,1,0,0,0,863, - 864,5,62,0,0,864,163,1,0,0,0,865,866,5,62,0,0,866,867,5,61,0,0,867,165, - 1,0,0,0,868,869,5,43,0,0,869,167,1,0,0,0,870,871,5,45,0,0,871,169,1,0,0, - 0,872,873,5,42,0,0,873,171,1,0,0,0,874,875,5,47,0,0,875,173,1,0,0,0,876, - 877,5,37,0,0,877,175,1,0,0,0,878,879,3,144,64,0,879,883,3,82,33,0,880,882, - 3,98,41,0,881,880,1,0,0,0,882,885,1,0,0,0,883,881,1,0,0,0,883,884,1,0,0, - 0,884,893,1,0,0,0,885,883,1,0,0,0,886,888,3,144,64,0,887,889,3,80,32,0, - 888,887,1,0,0,0,889,890,1,0,0,0,890,888,1,0,0,0,890,891,1,0,0,0,891,893, - 1,0,0,0,892,878,1,0,0,0,892,886,1,0,0,0,893,177,1,0,0,0,894,895,5,91,0, - 0,895,896,1,0,0,0,896,897,6,81,0,0,897,898,6,81,0,0,898,179,1,0,0,0,899, - 900,5,93,0,0,900,901,1,0,0,0,901,902,6,82,15,0,902,903,6,82,15,0,903,181, - 1,0,0,0,904,908,3,82,33,0,905,907,3,98,41,0,906,905,1,0,0,0,907,910,1,0, - 0,0,908,906,1,0,0,0,908,909,1,0,0,0,909,921,1,0,0,0,910,908,1,0,0,0,911, - 914,3,96,40,0,912,914,3,90,37,0,913,911,1,0,0,0,913,912,1,0,0,0,914,916, - 1,0,0,0,915,917,3,98,41,0,916,915,1,0,0,0,917,918,1,0,0,0,918,916,1,0,0, - 0,918,919,1,0,0,0,919,921,1,0,0,0,920,904,1,0,0,0,920,913,1,0,0,0,921,183, - 1,0,0,0,922,924,3,92,38,0,923,925,3,94,39,0,924,923,1,0,0,0,925,926,1,0, - 0,0,926,924,1,0,0,0,926,927,1,0,0,0,927,928,1,0,0,0,928,929,3,92,38,0,929, - 185,1,0,0,0,930,931,3,184,84,0,931,187,1,0,0,0,932,933,3,58,21,0,933,934, - 1,0,0,0,934,935,6,86,11,0,935,189,1,0,0,0,936,937,3,60,22,0,937,938,1,0, - 0,0,938,939,6,87,11,0,939,191,1,0,0,0,940,941,3,62,23,0,941,942,1,0,0,0, - 942,943,6,88,11,0,943,193,1,0,0,0,944,945,3,78,31,0,945,946,1,0,0,0,946, - 947,6,89,14,0,947,948,6,89,15,0,948,195,1,0,0,0,949,950,3,178,81,0,950, - 951,1,0,0,0,951,952,6,90,12,0,952,197,1,0,0,0,953,954,3,180,82,0,954,955, - 1,0,0,0,955,956,6,91,16,0,956,199,1,0,0,0,957,958,3,116,50,0,958,959,1, - 0,0,0,959,960,6,92,17,0,960,201,1,0,0,0,961,962,3,112,48,0,962,963,1,0, - 0,0,963,964,6,93,18,0,964,203,1,0,0,0,965,966,3,100,42,0,966,967,1,0,0, - 0,967,968,6,94,19,0,968,205,1,0,0,0,969,970,7,16,0,0,970,971,7,3,0,0,971, - 972,7,5,0,0,972,973,7,12,0,0,973,974,7,0,0,0,974,975,7,12,0,0,975,976,7, - 5,0,0,976,977,7,12,0,0,977,207,1,0,0,0,978,979,3,66,25,0,979,980,1,0,0, - 0,980,981,6,96,20,0,981,209,1,0,0,0,982,983,3,58,21,0,983,984,1,0,0,0,984, - 985,6,97,11,0,985,211,1,0,0,0,986,987,3,60,22,0,987,988,1,0,0,0,988,989, - 6,98,11,0,989,213,1,0,0,0,990,991,3,62,23,0,991,992,1,0,0,0,992,993,6,99, - 11,0,993,215,1,0,0,0,994,995,3,78,31,0,995,996,1,0,0,0,996,997,6,100,14, - 0,997,998,6,100,15,0,998,217,1,0,0,0,999,1000,3,120,52,0,1000,1001,1,0, - 0,0,1001,1002,6,101,21,0,1002,219,1,0,0,0,1003,1004,3,116,50,0,1004,1005, - 1,0,0,0,1005,1006,6,102,17,0,1006,221,1,0,0,0,1007,1012,3,82,33,0,1008, - 1012,3,80,32,0,1009,1012,3,96,40,0,1010,1012,3,170,77,0,1011,1007,1,0,0, - 0,1011,1008,1,0,0,0,1011,1009,1,0,0,0,1011,1010,1,0,0,0,1012,223,1,0,0, - 0,1013,1016,3,82,33,0,1014,1016,3,170,77,0,1015,1013,1,0,0,0,1015,1014, - 1,0,0,0,1016,1020,1,0,0,0,1017,1019,3,222,103,0,1018,1017,1,0,0,0,1019, - 1022,1,0,0,0,1020,1018,1,0,0,0,1020,1021,1,0,0,0,1021,1033,1,0,0,0,1022, - 1020,1,0,0,0,1023,1026,3,96,40,0,1024,1026,3,90,37,0,1025,1023,1,0,0,0, - 1025,1024,1,0,0,0,1026,1028,1,0,0,0,1027,1029,3,222,103,0,1028,1027,1,0, - 0,0,1029,1030,1,0,0,0,1030,1028,1,0,0,0,1030,1031,1,0,0,0,1031,1033,1,0, - 0,0,1032,1015,1,0,0,0,1032,1025,1,0,0,0,1033,225,1,0,0,0,1034,1037,3,224, - 104,0,1035,1037,3,184,84,0,1036,1034,1,0,0,0,1036,1035,1,0,0,0,1037,1038, - 1,0,0,0,1038,1036,1,0,0,0,1038,1039,1,0,0,0,1039,227,1,0,0,0,1040,1041, - 3,58,21,0,1041,1042,1,0,0,0,1042,1043,6,106,11,0,1043,229,1,0,0,0,1044, - 1045,3,60,22,0,1045,1046,1,0,0,0,1046,1047,6,107,11,0,1047,231,1,0,0,0, - 1048,1049,3,62,23,0,1049,1050,1,0,0,0,1050,1051,6,108,11,0,1051,233,1,0, - 0,0,1052,1053,3,78,31,0,1053,1054,1,0,0,0,1054,1055,6,109,14,0,1055,1056, - 6,109,15,0,1056,235,1,0,0,0,1057,1058,3,112,48,0,1058,1059,1,0,0,0,1059, - 1060,6,110,18,0,1060,237,1,0,0,0,1061,1062,3,116,50,0,1062,1063,1,0,0,0, - 1063,1064,6,111,17,0,1064,239,1,0,0,0,1065,1066,3,120,52,0,1066,1067,1, - 0,0,0,1067,1068,6,112,21,0,1068,241,1,0,0,0,1069,1070,7,12,0,0,1070,1071, - 7,2,0,0,1071,243,1,0,0,0,1072,1073,3,226,105,0,1073,1074,1,0,0,0,1074,1075, - 6,114,22,0,1075,245,1,0,0,0,1076,1077,3,58,21,0,1077,1078,1,0,0,0,1078, - 1079,6,115,11,0,1079,247,1,0,0,0,1080,1081,3,60,22,0,1081,1082,1,0,0,0, - 1082,1083,6,116,11,0,1083,249,1,0,0,0,1084,1085,3,62,23,0,1085,1086,1,0, - 0,0,1086,1087,6,117,11,0,1087,251,1,0,0,0,1088,1089,3,78,31,0,1089,1090, - 1,0,0,0,1090,1091,6,118,14,0,1091,1092,6,118,15,0,1092,253,1,0,0,0,1093, - 1094,3,178,81,0,1094,1095,1,0,0,0,1095,1096,6,119,12,0,1096,1097,6,119, - 23,0,1097,255,1,0,0,0,1098,1099,7,7,0,0,1099,1100,7,9,0,0,1100,1101,1,0, - 0,0,1101,1102,6,120,24,0,1102,257,1,0,0,0,1103,1104,7,20,0,0,1104,1105, - 7,1,0,0,1105,1106,7,5,0,0,1106,1107,7,10,0,0,1107,1108,1,0,0,0,1108,1109, - 6,121,24,0,1109,259,1,0,0,0,1110,1111,8,34,0,0,1111,261,1,0,0,0,1112,1114, - 3,260,122,0,1113,1112,1,0,0,0,1114,1115,1,0,0,0,1115,1113,1,0,0,0,1115, - 1116,1,0,0,0,1116,1117,1,0,0,0,1117,1118,3,360,172,0,1118,1120,1,0,0,0, - 1119,1113,1,0,0,0,1119,1120,1,0,0,0,1120,1122,1,0,0,0,1121,1123,3,260,122, - 0,1122,1121,1,0,0,0,1123,1124,1,0,0,0,1124,1122,1,0,0,0,1124,1125,1,0,0, - 0,1125,263,1,0,0,0,1126,1127,3,186,85,0,1127,1128,1,0,0,0,1128,1129,6,124, - 25,0,1129,265,1,0,0,0,1130,1131,3,262,123,0,1131,1132,1,0,0,0,1132,1133, - 6,125,26,0,1133,267,1,0,0,0,1134,1135,3,58,21,0,1135,1136,1,0,0,0,1136, - 1137,6,126,11,0,1137,269,1,0,0,0,1138,1139,3,60,22,0,1139,1140,1,0,0,0, - 1140,1141,6,127,11,0,1141,271,1,0,0,0,1142,1143,3,62,23,0,1143,1144,1,0, - 0,0,1144,1145,6,128,11,0,1145,273,1,0,0,0,1146,1147,3,78,31,0,1147,1148, - 1,0,0,0,1148,1149,6,129,14,0,1149,1150,6,129,15,0,1150,1151,6,129,15,0, - 1151,275,1,0,0,0,1152,1153,3,112,48,0,1153,1154,1,0,0,0,1154,1155,6,130, - 18,0,1155,277,1,0,0,0,1156,1157,3,116,50,0,1157,1158,1,0,0,0,1158,1159, - 6,131,17,0,1159,279,1,0,0,0,1160,1161,3,120,52,0,1161,1162,1,0,0,0,1162, - 1163,6,132,21,0,1163,281,1,0,0,0,1164,1165,3,258,121,0,1165,1166,1,0,0, - 0,1166,1167,6,133,27,0,1167,283,1,0,0,0,1168,1169,3,226,105,0,1169,1170, - 1,0,0,0,1170,1171,6,134,22,0,1171,285,1,0,0,0,1172,1173,3,186,85,0,1173, - 1174,1,0,0,0,1174,1175,6,135,25,0,1175,287,1,0,0,0,1176,1177,3,58,21,0, - 1177,1178,1,0,0,0,1178,1179,6,136,11,0,1179,289,1,0,0,0,1180,1181,3,60, - 22,0,1181,1182,1,0,0,0,1182,1183,6,137,11,0,1183,291,1,0,0,0,1184,1185, - 3,62,23,0,1185,1186,1,0,0,0,1186,1187,6,138,11,0,1187,293,1,0,0,0,1188, - 1189,3,78,31,0,1189,1190,1,0,0,0,1190,1191,6,139,14,0,1191,1192,6,139,15, - 0,1192,295,1,0,0,0,1193,1194,3,116,50,0,1194,1195,1,0,0,0,1195,1196,6,140, - 17,0,1196,297,1,0,0,0,1197,1198,3,120,52,0,1198,1199,1,0,0,0,1199,1200, - 6,141,21,0,1200,299,1,0,0,0,1201,1202,3,256,120,0,1202,1203,1,0,0,0,1203, - 1204,6,142,28,0,1204,1205,6,142,29,0,1205,301,1,0,0,0,1206,1207,3,66,25, - 0,1207,1208,1,0,0,0,1208,1209,6,143,20,0,1209,303,1,0,0,0,1210,1211,3,58, - 21,0,1211,1212,1,0,0,0,1212,1213,6,144,11,0,1213,305,1,0,0,0,1214,1215, - 3,60,22,0,1215,1216,1,0,0,0,1216,1217,6,145,11,0,1217,307,1,0,0,0,1218, - 1219,3,62,23,0,1219,1220,1,0,0,0,1220,1221,6,146,11,0,1221,309,1,0,0,0, - 1222,1223,3,78,31,0,1223,1224,1,0,0,0,1224,1225,6,147,14,0,1225,1226,6, - 147,15,0,1226,1227,6,147,15,0,1227,311,1,0,0,0,1228,1229,3,116,50,0,1229, - 1230,1,0,0,0,1230,1231,6,148,17,0,1231,313,1,0,0,0,1232,1233,3,120,52,0, - 1233,1234,1,0,0,0,1234,1235,6,149,21,0,1235,315,1,0,0,0,1236,1237,3,226, - 105,0,1237,1238,1,0,0,0,1238,1239,6,150,22,0,1239,317,1,0,0,0,1240,1241, - 3,58,21,0,1241,1242,1,0,0,0,1242,1243,6,151,11,0,1243,319,1,0,0,0,1244, - 1245,3,60,22,0,1245,1246,1,0,0,0,1246,1247,6,152,11,0,1247,321,1,0,0,0, - 1248,1249,3,62,23,0,1249,1250,1,0,0,0,1250,1251,6,153,11,0,1251,323,1,0, - 0,0,1252,1253,3,78,31,0,1253,1254,1,0,0,0,1254,1255,6,154,14,0,1255,1256, - 6,154,15,0,1256,325,1,0,0,0,1257,1258,3,120,52,0,1258,1259,1,0,0,0,1259, - 1260,6,155,21,0,1260,327,1,0,0,0,1261,1262,3,186,85,0,1262,1263,1,0,0,0, - 1263,1264,6,156,25,0,1264,329,1,0,0,0,1265,1266,3,182,83,0,1266,1267,1, - 0,0,0,1267,1268,6,157,30,0,1268,331,1,0,0,0,1269,1270,3,58,21,0,1270,1271, - 1,0,0,0,1271,1272,6,158,11,0,1272,333,1,0,0,0,1273,1274,3,60,22,0,1274, - 1275,1,0,0,0,1275,1276,6,159,11,0,1276,335,1,0,0,0,1277,1278,3,62,23,0, - 1278,1279,1,0,0,0,1279,1280,6,160,11,0,1280,337,1,0,0,0,1281,1282,3,78, - 31,0,1282,1283,1,0,0,0,1283,1284,6,161,14,0,1284,1285,6,161,15,0,1285,339, - 1,0,0,0,1286,1287,7,1,0,0,1287,1288,7,9,0,0,1288,1289,7,15,0,0,1289,1290, - 7,7,0,0,1290,341,1,0,0,0,1291,1292,3,58,21,0,1292,1293,1,0,0,0,1293,1294, - 6,163,11,0,1294,343,1,0,0,0,1295,1296,3,60,22,0,1296,1297,1,0,0,0,1297, - 1298,6,164,11,0,1298,345,1,0,0,0,1299,1300,3,62,23,0,1300,1301,1,0,0,0, - 1301,1302,6,165,11,0,1302,347,1,0,0,0,1303,1304,3,78,31,0,1304,1305,1,0, - 0,0,1305,1306,6,166,14,0,1306,1307,6,166,15,0,1307,349,1,0,0,0,1308,1309, - 7,15,0,0,1309,1310,7,19,0,0,1310,1311,7,9,0,0,1311,1312,7,4,0,0,1312,1313, - 7,5,0,0,1313,1314,7,1,0,0,1314,1315,7,7,0,0,1315,1316,7,9,0,0,1316,1317, - 7,2,0,0,1317,351,1,0,0,0,1318,1319,3,58,21,0,1319,1320,1,0,0,0,1320,1321, - 6,168,11,0,1321,353,1,0,0,0,1322,1323,3,60,22,0,1323,1324,1,0,0,0,1324, - 1325,6,169,11,0,1325,355,1,0,0,0,1326,1327,3,62,23,0,1327,1328,1,0,0,0, - 1328,1329,6,170,11,0,1329,357,1,0,0,0,1330,1331,3,180,82,0,1331,1332,1, - 0,0,0,1332,1333,6,171,16,0,1333,1334,6,171,15,0,1334,359,1,0,0,0,1335,1336, - 5,58,0,0,1336,361,1,0,0,0,1337,1343,3,90,37,0,1338,1343,3,80,32,0,1339, - 1343,3,120,52,0,1340,1343,3,82,33,0,1341,1343,3,96,40,0,1342,1337,1,0,0, - 0,1342,1338,1,0,0,0,1342,1339,1,0,0,0,1342,1340,1,0,0,0,1342,1341,1,0,0, - 0,1343,1344,1,0,0,0,1344,1342,1,0,0,0,1344,1345,1,0,0,0,1345,363,1,0,0, - 0,1346,1347,3,58,21,0,1347,1348,1,0,0,0,1348,1349,6,174,11,0,1349,365,1, - 0,0,0,1350,1351,3,60,22,0,1351,1352,1,0,0,0,1352,1353,6,175,11,0,1353,367, - 1,0,0,0,1354,1355,3,62,23,0,1355,1356,1,0,0,0,1356,1357,6,176,11,0,1357, - 369,1,0,0,0,1358,1359,3,78,31,0,1359,1360,1,0,0,0,1360,1361,6,177,14,0, - 1361,1362,6,177,15,0,1362,371,1,0,0,0,1363,1364,3,66,25,0,1364,1365,1,0, - 0,0,1365,1366,6,178,20,0,1366,1367,6,178,15,0,1367,1368,6,178,31,0,1368, - 373,1,0,0,0,1369,1370,3,58,21,0,1370,1371,1,0,0,0,1371,1372,6,179,11,0, - 1372,375,1,0,0,0,1373,1374,3,60,22,0,1374,1375,1,0,0,0,1375,1376,6,180, - 11,0,1376,377,1,0,0,0,1377,1378,3,62,23,0,1378,1379,1,0,0,0,1379,1380,6, - 181,11,0,1380,379,1,0,0,0,1381,1382,3,116,50,0,1382,1383,1,0,0,0,1383,1384, - 6,182,17,0,1384,1385,6,182,15,0,1385,1386,6,182,7,0,1386,381,1,0,0,0,1387, - 1388,3,58,21,0,1388,1389,1,0,0,0,1389,1390,6,183,11,0,1390,383,1,0,0,0, - 1391,1392,3,60,22,0,1392,1393,1,0,0,0,1393,1394,6,184,11,0,1394,385,1,0, - 0,0,1395,1396,3,62,23,0,1396,1397,1,0,0,0,1397,1398,6,185,11,0,1398,387, - 1,0,0,0,1399,1400,3,186,85,0,1400,1401,1,0,0,0,1401,1402,6,186,15,0,1402, - 1403,6,186,0,0,1403,1404,6,186,25,0,1404,389,1,0,0,0,1405,1406,3,182,83, - 0,1406,1407,1,0,0,0,1407,1408,6,187,15,0,1408,1409,6,187,0,0,1409,1410, - 6,187,30,0,1410,391,1,0,0,0,1411,1412,3,106,45,0,1412,1413,1,0,0,0,1413, - 1414,6,188,15,0,1414,1415,6,188,0,0,1415,1416,6,188,32,0,1416,393,1,0,0, - 0,1417,1418,3,78,31,0,1418,1419,1,0,0,0,1419,1420,6,189,14,0,1420,1421, - 6,189,15,0,1421,395,1,0,0,0,65,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,568, - 578,582,585,594,596,607,614,619,658,663,672,679,684,686,697,705,708,710, - 715,720,726,733,738,744,747,755,759,883,890,892,908,913,918,920,926,1011, - 1015,1020,1025,1030,1032,1036,1038,1115,1119,1124,1342,1344,33,5,2,0,5, + 2,187,7,187,2,188,7,188,2,189,7,189,2,190,7,190,2,191,7,191,2,192,7,192, + 2,193,7,193,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3, + 1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6, + 1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7, + 1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10, + 1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1, + 11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,13,1,13,1,13,1,13, + 1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1, + 14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1,16,1,16,1,16, + 1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1, + 18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,20,4,20,575,8,20,11,20, + 12,20,576,1,20,1,20,1,21,1,21,1,21,1,21,5,21,585,8,21,10,21,12,21,588,9, + 21,1,21,3,21,591,8,21,1,21,3,21,594,8,21,1,21,1,21,1,22,1,22,1,22,1,22, + 1,22,5,22,603,8,22,10,22,12,22,606,9,22,1,22,1,22,1,22,1,22,1,22,1,23,4, + 23,614,8,23,11,23,12,23,615,1,23,1,23,1,24,1,24,1,24,3,24,623,8,24,1,25, + 4,25,626,8,25,11,25,12,25,627,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1, + 27,1,27,1,28,1,28,1,28,1,28,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,31, + 1,31,1,31,1,31,1,32,1,32,1,33,1,33,1,34,1,34,1,34,1,35,1,35,1,36,1,36,3, + 36,667,8,36,1,36,4,36,670,8,36,11,36,12,36,671,1,37,1,37,1,38,1,38,1,39, + 1,39,1,39,3,39,681,8,39,1,40,1,40,1,41,1,41,1,41,3,41,688,8,41,1,42,1,42, + 1,42,5,42,693,8,42,10,42,12,42,696,9,42,1,42,1,42,1,42,1,42,1,42,1,42,5, + 42,704,8,42,10,42,12,42,707,9,42,1,42,1,42,1,42,1,42,1,42,3,42,714,8,42, + 1,42,3,42,717,8,42,3,42,719,8,42,1,43,4,43,722,8,43,11,43,12,43,723,1,44, + 4,44,727,8,44,11,44,12,44,728,1,44,1,44,5,44,733,8,44,10,44,12,44,736,9, + 44,1,44,1,44,4,44,740,8,44,11,44,12,44,741,1,44,4,44,745,8,44,11,44,12, + 44,746,1,44,1,44,5,44,751,8,44,10,44,12,44,754,9,44,3,44,756,8,44,1,44, + 1,44,1,44,1,44,4,44,762,8,44,11,44,12,44,763,1,44,1,44,3,44,768,8,44,1, + 45,1,45,1,45,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,48,1,48,1,49,1,49, + 1,49,1,50,1,50,1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,53,1,53,1,53,1,53,1, + 53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55,1,55,1,55,1,56,1,56, + 1,57,1,57,1,57,1,58,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,60,1,60,1,60,1, + 60,1,61,1,61,1,61,1,61,1,61,1,62,1,62,1,62,1,62,1,62,1,62,1,63,1,63,1,63, + 1,64,1,64,1,65,1,65,1,65,1,65,1,65,1,65,1,66,1,66,1,67,1,67,1,67,1,67,1, + 67,1,68,1,68,1,68,1,69,1,69,1,69,1,70,1,70,1,70,1,71,1,71,1,72,1,72,1,72, + 1,73,1,73,1,74,1,74,1,74,1,75,1,75,1,76,1,76,1,77,1,77,1,78,1,78,1,79,1, + 79,1,80,1,80,1,80,5,80,890,8,80,10,80,12,80,893,9,80,1,80,1,80,4,80,897, + 8,80,11,80,12,80,898,3,80,901,8,80,1,81,1,81,1,81,1,81,1,81,1,82,1,82,1, + 82,1,82,1,82,1,83,1,83,5,83,915,8,83,10,83,12,83,918,9,83,1,83,1,83,3,83, + 922,8,83,1,83,4,83,925,8,83,11,83,12,83,926,3,83,929,8,83,1,84,1,84,4,84, + 933,8,84,11,84,12,84,934,1,84,1,84,1,85,1,85,1,86,1,86,1,86,1,86,1,87,1, + 87,1,87,1,87,1,88,1,88,1,88,1,88,1,89,1,89,1,89,1,89,1,89,1,90,1,90,1,90, + 1,90,1,91,1,91,1,91,1,91,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1,93,1,94,1, + 94,1,94,1,94,1,95,1,95,1,95,1,95,1,95,1,95,1,95,1,95,1,95,1,96,1,96,1,96, + 1,96,1,97,1,97,1,97,1,97,1,98,1,98,1,98,1,98,1,99,1,99,1,99,1,99,1,100, + 1,100,1,100,1,100,1,101,1,101,1,101,1,101,1,101,1,102,1,102,1,102,1,102, + 1,103,1,103,1,103,1,103,1,104,1,104,1,104,1,104,3,104,1024,8,104,1,105, + 1,105,3,105,1028,8,105,1,105,5,105,1031,8,105,10,105,12,105,1034,9,105, + 1,105,1,105,3,105,1038,8,105,1,105,4,105,1041,8,105,11,105,12,105,1042, + 3,105,1045,8,105,1,106,1,106,4,106,1049,8,106,11,106,12,106,1050,1,107, + 1,107,1,107,1,107,1,108,1,108,1,108,1,108,1,109,1,109,1,109,1,109,1,110, + 1,110,1,110,1,110,1,110,1,111,1,111,1,111,1,111,1,112,1,112,1,112,1,112, + 1,113,1,113,1,113,1,113,1,114,1,114,1,114,1,115,1,115,1,115,1,115,1,116, + 1,116,1,116,1,116,1,117,1,117,1,117,1,117,1,118,1,118,1,118,1,118,1,119, + 1,119,1,119,1,119,1,119,1,120,1,120,1,120,1,120,1,120,1,121,1,121,1,121, + 1,121,1,121,1,122,1,122,1,122,1,122,1,122,1,122,1,122,1,123,1,123,1,124, + 4,124,1126,8,124,11,124,12,124,1127,1,124,1,124,3,124,1132,8,124,1,124, + 4,124,1135,8,124,11,124,12,124,1136,1,125,1,125,1,125,1,125,1,126,1,126, + 1,126,1,126,1,127,1,127,1,127,1,127,1,128,1,128,1,128,1,128,1,129,1,129, + 1,129,1,129,1,129,1,129,1,130,1,130,1,130,1,130,1,131,1,131,1,131,1,131, + 1,132,1,132,1,132,1,132,1,133,1,133,1,133,1,133,1,134,1,134,1,134,1,134, + 1,135,1,135,1,135,1,135,1,136,1,136,1,136,1,136,1,137,1,137,1,137,1,137, + 1,138,1,138,1,138,1,138,1,139,1,139,1,139,1,139,1,139,1,140,1,140,1,140, + 1,140,1,141,1,141,1,141,1,141,1,142,1,142,1,142,1,142,1,143,1,143,1,143, + 1,143,1,143,1,144,1,144,1,144,1,144,1,145,1,145,1,145,1,145,1,146,1,146, + 1,146,1,146,1,147,1,147,1,147,1,147,1,148,1,148,1,148,1,148,1,149,1,149, + 1,149,1,149,1,149,1,149,1,150,1,150,1,150,1,150,1,151,1,151,1,151,1,151, + 1,152,1,152,1,152,1,152,1,153,1,153,1,153,1,153,1,154,1,154,1,154,1,154, + 1,155,1,155,1,155,1,155,1,156,1,156,1,156,1,156,1,156,1,157,1,157,1,157, + 1,157,1,158,1,158,1,158,1,158,1,159,1,159,1,159,1,159,1,160,1,160,1,160, + 1,160,1,161,1,161,1,161,1,161,1,162,1,162,1,162,1,162,1,163,1,163,1,163, + 1,163,1,163,1,164,1,164,1,164,1,164,1,164,1,165,1,165,1,165,1,165,1,166, + 1,166,1,166,1,166,1,167,1,167,1,167,1,167,1,168,1,168,1,168,1,168,1,168, + 1,169,1,169,1,169,1,169,1,169,1,169,1,169,1,169,1,169,1,169,1,170,1,170, + 1,170,1,170,1,171,1,171,1,171,1,171,1,172,1,172,1,172,1,172,1,173,1,173, + 1,173,1,173,1,173,1,174,1,174,1,175,1,175,1,175,1,175,1,175,4,175,1359, + 8,175,11,175,12,175,1360,1,176,1,176,1,176,1,176,1,177,1,177,1,177,1,177, + 1,178,1,178,1,178,1,178,1,179,1,179,1,179,1,179,1,179,1,180,1,180,1,180, + 1,180,1,180,1,180,1,181,1,181,1,181,1,181,1,181,1,181,1,182,1,182,1,182, + 1,182,1,183,1,183,1,183,1,183,1,184,1,184,1,184,1,184,1,185,1,185,1,185, + 1,185,1,185,1,185,1,186,1,186,1,186,1,186,1,186,1,186,1,187,1,187,1,187, + 1,187,1,188,1,188,1,188,1,188,1,189,1,189,1,189,1,189,1,190,1,190,1,190, + 1,190,1,190,1,190,1,191,1,191,1,191,1,191,1,191,1,191,1,192,1,192,1,192, + 1,192,1,192,1,192,1,193,1,193,1,193,1,193,1,193,2,604,705,0,194,16,1,18, + 2,20,3,22,4,24,5,26,6,28,7,30,8,32,9,34,10,36,11,38,12,40,13,42,14,44,15, + 46,16,48,17,50,18,52,19,54,20,56,21,58,22,60,23,62,24,64,0,66,25,68,0,70, + 0,72,26,74,27,76,28,78,29,80,0,82,0,84,0,86,0,88,0,90,0,92,0,94,0,96,0, + 98,0,100,30,102,31,104,32,106,33,108,34,110,35,112,36,114,37,116,38,118, + 39,120,40,122,41,124,42,126,43,128,44,130,45,132,46,134,47,136,48,138,49, + 140,50,142,51,144,52,146,53,148,54,150,55,152,56,154,57,156,58,158,59,160, + 60,162,61,164,62,166,63,168,64,170,65,172,66,174,67,176,68,178,69,180,70, + 182,71,184,0,186,72,188,73,190,74,192,75,194,0,196,0,198,0,200,0,202,0, + 204,0,206,76,208,0,210,0,212,77,214,78,216,79,218,0,220,0,222,0,224,0,226, + 0,228,80,230,81,232,82,234,83,236,0,238,0,240,0,242,0,244,84,246,0,248, + 85,250,86,252,87,254,0,256,0,258,88,260,89,262,0,264,90,266,0,268,91,270, + 92,272,93,274,0,276,0,278,0,280,0,282,0,284,0,286,0,288,94,290,95,292,96, + 294,0,296,0,298,0,300,0,302,0,304,0,306,0,308,97,310,98,312,99,314,0,316, + 0,318,0,320,0,322,100,324,101,326,102,328,0,330,0,332,0,334,0,336,103,338, + 104,340,105,342,0,344,106,346,107,348,108,350,109,352,0,354,110,356,111, + 358,112,360,113,362,0,364,114,366,115,368,116,370,117,372,118,374,0,376, + 0,378,0,380,119,382,120,384,121,386,0,388,0,390,122,392,123,394,124,396, + 0,398,0,400,0,402,0,16,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,35,2,0,68, + 68,100,100,2,0,73,73,105,105,2,0,83,83,115,115,2,0,69,69,101,101,2,0,67, + 67,99,99,2,0,84,84,116,116,2,0,82,82,114,114,2,0,79,79,111,111,2,0,80,80, + 112,112,2,0,78,78,110,110,2,0,72,72,104,104,2,0,86,86,118,118,2,0,65,65, + 97,97,2,0,76,76,108,108,2,0,88,88,120,120,2,0,70,70,102,102,2,0,77,77,109, + 109,2,0,71,71,103,103,2,0,75,75,107,107,2,0,85,85,117,117,2,0,87,87,119, + 119,6,0,9,10,13,13,32,32,47,47,91,91,93,93,2,0,10,10,13,13,3,0,9,10,13, + 13,32,32,11,0,9,10,13,13,32,32,34,34,44,44,47,47,58,58,61,61,91,91,93,93, + 124,124,2,0,42,42,47,47,1,0,48,57,2,0,65,90,97,122,8,0,34,34,78,78,82,82, + 84,84,92,92,110,110,114,114,116,116,4,0,10,10,13,13,34,34,92,92,2,0,43, + 43,45,45,1,0,96,96,2,0,66,66,98,98,2,0,89,89,121,121,11,0,9,10,13,13,32, + 32,34,35,44,44,47,47,58,58,60,60,62,63,92,92,124,124,1476,0,16,1,0,0,0, + 0,18,1,0,0,0,0,20,1,0,0,0,0,22,1,0,0,0,0,24,1,0,0,0,0,26,1,0,0,0,0,28,1, + 0,0,0,0,30,1,0,0,0,0,32,1,0,0,0,0,34,1,0,0,0,0,36,1,0,0,0,0,38,1,0,0,0, + 0,40,1,0,0,0,0,42,1,0,0,0,0,44,1,0,0,0,0,46,1,0,0,0,0,48,1,0,0,0,0,50,1, + 0,0,0,0,52,1,0,0,0,0,54,1,0,0,0,0,56,1,0,0,0,0,58,1,0,0,0,0,60,1,0,0,0, + 0,62,1,0,0,0,0,66,1,0,0,0,1,68,1,0,0,0,1,70,1,0,0,0,1,72,1,0,0,0,1,74,1, + 0,0,0,1,76,1,0,0,0,2,78,1,0,0,0,2,100,1,0,0,0,2,102,1,0,0,0,2,104,1,0,0, + 0,2,106,1,0,0,0,2,108,1,0,0,0,2,110,1,0,0,0,2,112,1,0,0,0,2,114,1,0,0,0, + 2,116,1,0,0,0,2,118,1,0,0,0,2,120,1,0,0,0,2,122,1,0,0,0,2,124,1,0,0,0,2, + 126,1,0,0,0,2,128,1,0,0,0,2,130,1,0,0,0,2,132,1,0,0,0,2,134,1,0,0,0,2,136, + 1,0,0,0,2,138,1,0,0,0,2,140,1,0,0,0,2,142,1,0,0,0,2,144,1,0,0,0,2,146,1, + 0,0,0,2,148,1,0,0,0,2,150,1,0,0,0,2,152,1,0,0,0,2,154,1,0,0,0,2,156,1,0, + 0,0,2,158,1,0,0,0,2,160,1,0,0,0,2,162,1,0,0,0,2,164,1,0,0,0,2,166,1,0,0, + 0,2,168,1,0,0,0,2,170,1,0,0,0,2,172,1,0,0,0,2,174,1,0,0,0,2,176,1,0,0,0, + 2,178,1,0,0,0,2,180,1,0,0,0,2,182,1,0,0,0,2,186,1,0,0,0,2,188,1,0,0,0,2, + 190,1,0,0,0,2,192,1,0,0,0,3,194,1,0,0,0,3,196,1,0,0,0,3,198,1,0,0,0,3,200, + 1,0,0,0,3,202,1,0,0,0,3,204,1,0,0,0,3,206,1,0,0,0,3,208,1,0,0,0,3,210,1, + 0,0,0,3,212,1,0,0,0,3,214,1,0,0,0,3,216,1,0,0,0,4,218,1,0,0,0,4,220,1,0, + 0,0,4,222,1,0,0,0,4,228,1,0,0,0,4,230,1,0,0,0,4,232,1,0,0,0,4,234,1,0,0, + 0,5,236,1,0,0,0,5,238,1,0,0,0,5,240,1,0,0,0,5,242,1,0,0,0,5,244,1,0,0,0, + 5,246,1,0,0,0,5,248,1,0,0,0,5,250,1,0,0,0,5,252,1,0,0,0,6,254,1,0,0,0,6, + 256,1,0,0,0,6,258,1,0,0,0,6,260,1,0,0,0,6,264,1,0,0,0,6,266,1,0,0,0,6,268, + 1,0,0,0,6,270,1,0,0,0,6,272,1,0,0,0,7,274,1,0,0,0,7,276,1,0,0,0,7,278,1, + 0,0,0,7,280,1,0,0,0,7,282,1,0,0,0,7,284,1,0,0,0,7,286,1,0,0,0,7,288,1,0, + 0,0,7,290,1,0,0,0,7,292,1,0,0,0,8,294,1,0,0,0,8,296,1,0,0,0,8,298,1,0,0, + 0,8,300,1,0,0,0,8,302,1,0,0,0,8,304,1,0,0,0,8,306,1,0,0,0,8,308,1,0,0,0, + 8,310,1,0,0,0,8,312,1,0,0,0,9,314,1,0,0,0,9,316,1,0,0,0,9,318,1,0,0,0,9, + 320,1,0,0,0,9,322,1,0,0,0,9,324,1,0,0,0,9,326,1,0,0,0,10,328,1,0,0,0,10, + 330,1,0,0,0,10,332,1,0,0,0,10,334,1,0,0,0,10,336,1,0,0,0,10,338,1,0,0,0, + 10,340,1,0,0,0,11,342,1,0,0,0,11,344,1,0,0,0,11,346,1,0,0,0,11,348,1,0, + 0,0,11,350,1,0,0,0,12,352,1,0,0,0,12,354,1,0,0,0,12,356,1,0,0,0,12,358, + 1,0,0,0,12,360,1,0,0,0,13,362,1,0,0,0,13,364,1,0,0,0,13,366,1,0,0,0,13, + 368,1,0,0,0,13,370,1,0,0,0,13,372,1,0,0,0,14,374,1,0,0,0,14,376,1,0,0,0, + 14,378,1,0,0,0,14,380,1,0,0,0,14,382,1,0,0,0,14,384,1,0,0,0,15,386,1,0, + 0,0,15,388,1,0,0,0,15,390,1,0,0,0,15,392,1,0,0,0,15,394,1,0,0,0,15,396, + 1,0,0,0,15,398,1,0,0,0,15,400,1,0,0,0,15,402,1,0,0,0,16,404,1,0,0,0,18, + 414,1,0,0,0,20,421,1,0,0,0,22,430,1,0,0,0,24,437,1,0,0,0,26,447,1,0,0,0, + 28,454,1,0,0,0,30,461,1,0,0,0,32,475,1,0,0,0,34,482,1,0,0,0,36,490,1,0, + 0,0,38,499,1,0,0,0,40,506,1,0,0,0,42,516,1,0,0,0,44,528,1,0,0,0,46,537, + 1,0,0,0,48,543,1,0,0,0,50,550,1,0,0,0,52,557,1,0,0,0,54,565,1,0,0,0,56, + 574,1,0,0,0,58,580,1,0,0,0,60,597,1,0,0,0,62,613,1,0,0,0,64,622,1,0,0,0, + 66,625,1,0,0,0,68,629,1,0,0,0,70,634,1,0,0,0,72,639,1,0,0,0,74,643,1,0, + 0,0,76,647,1,0,0,0,78,651,1,0,0,0,80,655,1,0,0,0,82,657,1,0,0,0,84,659, + 1,0,0,0,86,662,1,0,0,0,88,664,1,0,0,0,90,673,1,0,0,0,92,675,1,0,0,0,94, + 680,1,0,0,0,96,682,1,0,0,0,98,687,1,0,0,0,100,718,1,0,0,0,102,721,1,0,0, + 0,104,767,1,0,0,0,106,769,1,0,0,0,108,772,1,0,0,0,110,776,1,0,0,0,112,780, + 1,0,0,0,114,782,1,0,0,0,116,785,1,0,0,0,118,787,1,0,0,0,120,792,1,0,0,0, + 122,794,1,0,0,0,124,800,1,0,0,0,126,806,1,0,0,0,128,811,1,0,0,0,130,813, + 1,0,0,0,132,816,1,0,0,0,134,819,1,0,0,0,136,824,1,0,0,0,138,828,1,0,0,0, + 140,833,1,0,0,0,142,839,1,0,0,0,144,842,1,0,0,0,146,844,1,0,0,0,148,850, + 1,0,0,0,150,852,1,0,0,0,152,857,1,0,0,0,154,860,1,0,0,0,156,863,1,0,0,0, + 158,866,1,0,0,0,160,868,1,0,0,0,162,871,1,0,0,0,164,873,1,0,0,0,166,876, + 1,0,0,0,168,878,1,0,0,0,170,880,1,0,0,0,172,882,1,0,0,0,174,884,1,0,0,0, + 176,900,1,0,0,0,178,902,1,0,0,0,180,907,1,0,0,0,182,928,1,0,0,0,184,930, + 1,0,0,0,186,938,1,0,0,0,188,940,1,0,0,0,190,944,1,0,0,0,192,948,1,0,0,0, + 194,952,1,0,0,0,196,957,1,0,0,0,198,961,1,0,0,0,200,965,1,0,0,0,202,969, + 1,0,0,0,204,973,1,0,0,0,206,977,1,0,0,0,208,986,1,0,0,0,210,990,1,0,0,0, + 212,994,1,0,0,0,214,998,1,0,0,0,216,1002,1,0,0,0,218,1006,1,0,0,0,220,1011, + 1,0,0,0,222,1015,1,0,0,0,224,1023,1,0,0,0,226,1044,1,0,0,0,228,1048,1,0, + 0,0,230,1052,1,0,0,0,232,1056,1,0,0,0,234,1060,1,0,0,0,236,1064,1,0,0,0, + 238,1069,1,0,0,0,240,1073,1,0,0,0,242,1077,1,0,0,0,244,1081,1,0,0,0,246, + 1084,1,0,0,0,248,1088,1,0,0,0,250,1092,1,0,0,0,252,1096,1,0,0,0,254,1100, + 1,0,0,0,256,1105,1,0,0,0,258,1110,1,0,0,0,260,1115,1,0,0,0,262,1122,1,0, + 0,0,264,1131,1,0,0,0,266,1138,1,0,0,0,268,1142,1,0,0,0,270,1146,1,0,0,0, + 272,1150,1,0,0,0,274,1154,1,0,0,0,276,1160,1,0,0,0,278,1164,1,0,0,0,280, + 1168,1,0,0,0,282,1172,1,0,0,0,284,1176,1,0,0,0,286,1180,1,0,0,0,288,1184, + 1,0,0,0,290,1188,1,0,0,0,292,1192,1,0,0,0,294,1196,1,0,0,0,296,1201,1,0, + 0,0,298,1205,1,0,0,0,300,1209,1,0,0,0,302,1213,1,0,0,0,304,1218,1,0,0,0, + 306,1222,1,0,0,0,308,1226,1,0,0,0,310,1230,1,0,0,0,312,1234,1,0,0,0,314, + 1238,1,0,0,0,316,1244,1,0,0,0,318,1248,1,0,0,0,320,1252,1,0,0,0,322,1256, + 1,0,0,0,324,1260,1,0,0,0,326,1264,1,0,0,0,328,1268,1,0,0,0,330,1273,1,0, + 0,0,332,1277,1,0,0,0,334,1281,1,0,0,0,336,1285,1,0,0,0,338,1289,1,0,0,0, + 340,1293,1,0,0,0,342,1297,1,0,0,0,344,1302,1,0,0,0,346,1307,1,0,0,0,348, + 1311,1,0,0,0,350,1315,1,0,0,0,352,1319,1,0,0,0,354,1324,1,0,0,0,356,1334, + 1,0,0,0,358,1338,1,0,0,0,360,1342,1,0,0,0,362,1346,1,0,0,0,364,1351,1,0, + 0,0,366,1358,1,0,0,0,368,1362,1,0,0,0,370,1366,1,0,0,0,372,1370,1,0,0,0, + 374,1374,1,0,0,0,376,1379,1,0,0,0,378,1385,1,0,0,0,380,1391,1,0,0,0,382, + 1395,1,0,0,0,384,1399,1,0,0,0,386,1403,1,0,0,0,388,1409,1,0,0,0,390,1415, + 1,0,0,0,392,1419,1,0,0,0,394,1423,1,0,0,0,396,1427,1,0,0,0,398,1433,1,0, + 0,0,400,1439,1,0,0,0,402,1445,1,0,0,0,404,405,7,0,0,0,405,406,7,1,0,0,406, + 407,7,2,0,0,407,408,7,2,0,0,408,409,7,3,0,0,409,410,7,4,0,0,410,411,7,5, + 0,0,411,412,1,0,0,0,412,413,6,0,0,0,413,17,1,0,0,0,414,415,7,0,0,0,415, + 416,7,6,0,0,416,417,7,7,0,0,417,418,7,8,0,0,418,419,1,0,0,0,419,420,6,1, + 1,0,420,19,1,0,0,0,421,422,7,3,0,0,422,423,7,9,0,0,423,424,7,6,0,0,424, + 425,7,1,0,0,425,426,7,4,0,0,426,427,7,10,0,0,427,428,1,0,0,0,428,429,6, + 2,2,0,429,21,1,0,0,0,430,431,7,3,0,0,431,432,7,11,0,0,432,433,7,12,0,0, + 433,434,7,13,0,0,434,435,1,0,0,0,435,436,6,3,0,0,436,23,1,0,0,0,437,438, + 7,3,0,0,438,439,7,14,0,0,439,440,7,8,0,0,440,441,7,13,0,0,441,442,7,12, + 0,0,442,443,7,1,0,0,443,444,7,9,0,0,444,445,1,0,0,0,445,446,6,4,3,0,446, + 25,1,0,0,0,447,448,7,15,0,0,448,449,7,6,0,0,449,450,7,7,0,0,450,451,7,16, + 0,0,451,452,1,0,0,0,452,453,6,5,4,0,453,27,1,0,0,0,454,455,7,17,0,0,455, + 456,7,6,0,0,456,457,7,7,0,0,457,458,7,18,0,0,458,459,1,0,0,0,459,460,6, + 6,0,0,460,29,1,0,0,0,461,462,7,1,0,0,462,463,7,9,0,0,463,464,7,13,0,0,464, + 465,7,1,0,0,465,466,7,9,0,0,466,467,7,3,0,0,467,468,7,2,0,0,468,469,7,5, + 0,0,469,470,7,12,0,0,470,471,7,5,0,0,471,472,7,2,0,0,472,473,1,0,0,0,473, + 474,6,7,0,0,474,31,1,0,0,0,475,476,7,18,0,0,476,477,7,3,0,0,477,478,7,3, + 0,0,478,479,7,8,0,0,479,480,1,0,0,0,480,481,6,8,1,0,481,33,1,0,0,0,482, + 483,7,13,0,0,483,484,7,1,0,0,484,485,7,16,0,0,485,486,7,1,0,0,486,487,7, + 5,0,0,487,488,1,0,0,0,488,489,6,9,0,0,489,35,1,0,0,0,490,491,7,13,0,0,491, + 492,7,7,0,0,492,493,7,7,0,0,493,494,7,18,0,0,494,495,7,19,0,0,495,496,7, + 8,0,0,496,497,1,0,0,0,497,498,6,10,5,0,498,37,1,0,0,0,499,500,7,16,0,0, + 500,501,7,3,0,0,501,502,7,5,0,0,502,503,7,12,0,0,503,504,1,0,0,0,504,505, + 6,11,6,0,505,39,1,0,0,0,506,507,7,16,0,0,507,508,7,3,0,0,508,509,7,5,0, + 0,509,510,7,6,0,0,510,511,7,1,0,0,511,512,7,4,0,0,512,513,7,2,0,0,513,514, + 1,0,0,0,514,515,6,12,7,0,515,41,1,0,0,0,516,517,7,16,0,0,517,518,7,11,0, + 0,518,519,5,95,0,0,519,520,7,3,0,0,520,521,7,14,0,0,521,522,7,8,0,0,522, + 523,7,12,0,0,523,524,7,9,0,0,524,525,7,0,0,0,525,526,1,0,0,0,526,527,6, + 13,8,0,527,43,1,0,0,0,528,529,7,6,0,0,529,530,7,3,0,0,530,531,7,9,0,0,531, + 532,7,12,0,0,532,533,7,16,0,0,533,534,7,3,0,0,534,535,1,0,0,0,535,536,6, + 14,9,0,536,45,1,0,0,0,537,538,7,6,0,0,538,539,7,7,0,0,539,540,7,20,0,0, + 540,541,1,0,0,0,541,542,6,15,0,0,542,47,1,0,0,0,543,544,7,2,0,0,544,545, + 7,10,0,0,545,546,7,7,0,0,546,547,7,20,0,0,547,548,1,0,0,0,548,549,6,16, + 10,0,549,49,1,0,0,0,550,551,7,2,0,0,551,552,7,7,0,0,552,553,7,6,0,0,553, + 554,7,5,0,0,554,555,1,0,0,0,555,556,6,17,0,0,556,51,1,0,0,0,557,558,7,2, + 0,0,558,559,7,5,0,0,559,560,7,12,0,0,560,561,7,5,0,0,561,562,7,2,0,0,562, + 563,1,0,0,0,563,564,6,18,0,0,564,53,1,0,0,0,565,566,7,20,0,0,566,567,7, + 10,0,0,567,568,7,3,0,0,568,569,7,6,0,0,569,570,7,3,0,0,570,571,1,0,0,0, + 571,572,6,19,0,0,572,55,1,0,0,0,573,575,8,21,0,0,574,573,1,0,0,0,575,576, + 1,0,0,0,576,574,1,0,0,0,576,577,1,0,0,0,577,578,1,0,0,0,578,579,6,20,0, + 0,579,57,1,0,0,0,580,581,5,47,0,0,581,582,5,47,0,0,582,586,1,0,0,0,583, + 585,8,22,0,0,584,583,1,0,0,0,585,588,1,0,0,0,586,584,1,0,0,0,586,587,1, + 0,0,0,587,590,1,0,0,0,588,586,1,0,0,0,589,591,5,13,0,0,590,589,1,0,0,0, + 590,591,1,0,0,0,591,593,1,0,0,0,592,594,5,10,0,0,593,592,1,0,0,0,593,594, + 1,0,0,0,594,595,1,0,0,0,595,596,6,21,11,0,596,59,1,0,0,0,597,598,5,47,0, + 0,598,599,5,42,0,0,599,604,1,0,0,0,600,603,3,60,22,0,601,603,9,0,0,0,602, + 600,1,0,0,0,602,601,1,0,0,0,603,606,1,0,0,0,604,605,1,0,0,0,604,602,1,0, + 0,0,605,607,1,0,0,0,606,604,1,0,0,0,607,608,5,42,0,0,608,609,5,47,0,0,609, + 610,1,0,0,0,610,611,6,22,11,0,611,61,1,0,0,0,612,614,7,23,0,0,613,612,1, + 0,0,0,614,615,1,0,0,0,615,613,1,0,0,0,615,616,1,0,0,0,616,617,1,0,0,0,617, + 618,6,23,11,0,618,63,1,0,0,0,619,623,8,24,0,0,620,621,5,47,0,0,621,623, + 8,25,0,0,622,619,1,0,0,0,622,620,1,0,0,0,623,65,1,0,0,0,624,626,3,64,24, + 0,625,624,1,0,0,0,626,627,1,0,0,0,627,625,1,0,0,0,627,628,1,0,0,0,628,67, + 1,0,0,0,629,630,3,178,81,0,630,631,1,0,0,0,631,632,6,26,12,0,632,633,6, + 26,13,0,633,69,1,0,0,0,634,635,3,78,31,0,635,636,1,0,0,0,636,637,6,27,14, + 0,637,638,6,27,15,0,638,71,1,0,0,0,639,640,3,62,23,0,640,641,1,0,0,0,641, + 642,6,28,11,0,642,73,1,0,0,0,643,644,3,58,21,0,644,645,1,0,0,0,645,646, + 6,29,11,0,646,75,1,0,0,0,647,648,3,60,22,0,648,649,1,0,0,0,649,650,6,30, + 11,0,650,77,1,0,0,0,651,652,5,124,0,0,652,653,1,0,0,0,653,654,6,31,15,0, + 654,79,1,0,0,0,655,656,7,26,0,0,656,81,1,0,0,0,657,658,7,27,0,0,658,83, + 1,0,0,0,659,660,5,92,0,0,660,661,7,28,0,0,661,85,1,0,0,0,662,663,8,29,0, + 0,663,87,1,0,0,0,664,666,7,3,0,0,665,667,7,30,0,0,666,665,1,0,0,0,666,667, + 1,0,0,0,667,669,1,0,0,0,668,670,3,80,32,0,669,668,1,0,0,0,670,671,1,0,0, + 0,671,669,1,0,0,0,671,672,1,0,0,0,672,89,1,0,0,0,673,674,5,64,0,0,674,91, + 1,0,0,0,675,676,5,96,0,0,676,93,1,0,0,0,677,681,8,31,0,0,678,679,5,96,0, + 0,679,681,5,96,0,0,680,677,1,0,0,0,680,678,1,0,0,0,681,95,1,0,0,0,682,683, + 5,95,0,0,683,97,1,0,0,0,684,688,3,82,33,0,685,688,3,80,32,0,686,688,3,96, + 40,0,687,684,1,0,0,0,687,685,1,0,0,0,687,686,1,0,0,0,688,99,1,0,0,0,689, + 694,5,34,0,0,690,693,3,84,34,0,691,693,3,86,35,0,692,690,1,0,0,0,692,691, + 1,0,0,0,693,696,1,0,0,0,694,692,1,0,0,0,694,695,1,0,0,0,695,697,1,0,0,0, + 696,694,1,0,0,0,697,719,5,34,0,0,698,699,5,34,0,0,699,700,5,34,0,0,700, + 701,5,34,0,0,701,705,1,0,0,0,702,704,8,22,0,0,703,702,1,0,0,0,704,707,1, + 0,0,0,705,706,1,0,0,0,705,703,1,0,0,0,706,708,1,0,0,0,707,705,1,0,0,0,708, + 709,5,34,0,0,709,710,5,34,0,0,710,711,5,34,0,0,711,713,1,0,0,0,712,714, + 5,34,0,0,713,712,1,0,0,0,713,714,1,0,0,0,714,716,1,0,0,0,715,717,5,34,0, + 0,716,715,1,0,0,0,716,717,1,0,0,0,717,719,1,0,0,0,718,689,1,0,0,0,718,698, + 1,0,0,0,719,101,1,0,0,0,720,722,3,80,32,0,721,720,1,0,0,0,722,723,1,0,0, + 0,723,721,1,0,0,0,723,724,1,0,0,0,724,103,1,0,0,0,725,727,3,80,32,0,726, + 725,1,0,0,0,727,728,1,0,0,0,728,726,1,0,0,0,728,729,1,0,0,0,729,730,1,0, + 0,0,730,734,3,120,52,0,731,733,3,80,32,0,732,731,1,0,0,0,733,736,1,0,0, + 0,734,732,1,0,0,0,734,735,1,0,0,0,735,768,1,0,0,0,736,734,1,0,0,0,737,739, + 3,120,52,0,738,740,3,80,32,0,739,738,1,0,0,0,740,741,1,0,0,0,741,739,1, + 0,0,0,741,742,1,0,0,0,742,768,1,0,0,0,743,745,3,80,32,0,744,743,1,0,0,0, + 745,746,1,0,0,0,746,744,1,0,0,0,746,747,1,0,0,0,747,755,1,0,0,0,748,752, + 3,120,52,0,749,751,3,80,32,0,750,749,1,0,0,0,751,754,1,0,0,0,752,750,1, + 0,0,0,752,753,1,0,0,0,753,756,1,0,0,0,754,752,1,0,0,0,755,748,1,0,0,0,755, + 756,1,0,0,0,756,757,1,0,0,0,757,758,3,88,36,0,758,768,1,0,0,0,759,761,3, + 120,52,0,760,762,3,80,32,0,761,760,1,0,0,0,762,763,1,0,0,0,763,761,1,0, + 0,0,763,764,1,0,0,0,764,765,1,0,0,0,765,766,3,88,36,0,766,768,1,0,0,0,767, + 726,1,0,0,0,767,737,1,0,0,0,767,744,1,0,0,0,767,759,1,0,0,0,768,105,1,0, + 0,0,769,770,7,32,0,0,770,771,7,33,0,0,771,107,1,0,0,0,772,773,7,12,0,0, + 773,774,7,9,0,0,774,775,7,0,0,0,775,109,1,0,0,0,776,777,7,12,0,0,777,778, + 7,2,0,0,778,779,7,4,0,0,779,111,1,0,0,0,780,781,5,61,0,0,781,113,1,0,0, + 0,782,783,5,58,0,0,783,784,5,58,0,0,784,115,1,0,0,0,785,786,5,44,0,0,786, + 117,1,0,0,0,787,788,7,0,0,0,788,789,7,3,0,0,789,790,7,2,0,0,790,791,7,4, + 0,0,791,119,1,0,0,0,792,793,5,46,0,0,793,121,1,0,0,0,794,795,7,15,0,0,795, + 796,7,12,0,0,796,797,7,13,0,0,797,798,7,2,0,0,798,799,7,3,0,0,799,123,1, + 0,0,0,800,801,7,15,0,0,801,802,7,1,0,0,802,803,7,6,0,0,803,804,7,2,0,0, + 804,805,7,5,0,0,805,125,1,0,0,0,806,807,7,13,0,0,807,808,7,12,0,0,808,809, + 7,2,0,0,809,810,7,5,0,0,810,127,1,0,0,0,811,812,5,40,0,0,812,129,1,0,0, + 0,813,814,7,1,0,0,814,815,7,9,0,0,815,131,1,0,0,0,816,817,7,1,0,0,817,818, + 7,2,0,0,818,133,1,0,0,0,819,820,7,13,0,0,820,821,7,1,0,0,821,822,7,18,0, + 0,822,823,7,3,0,0,823,135,1,0,0,0,824,825,7,9,0,0,825,826,7,7,0,0,826,827, + 7,5,0,0,827,137,1,0,0,0,828,829,7,9,0,0,829,830,7,19,0,0,830,831,7,13,0, + 0,831,832,7,13,0,0,832,139,1,0,0,0,833,834,7,9,0,0,834,835,7,19,0,0,835, + 836,7,13,0,0,836,837,7,13,0,0,837,838,7,2,0,0,838,141,1,0,0,0,839,840,7, + 7,0,0,840,841,7,6,0,0,841,143,1,0,0,0,842,843,5,63,0,0,843,145,1,0,0,0, + 844,845,7,6,0,0,845,846,7,13,0,0,846,847,7,1,0,0,847,848,7,18,0,0,848,849, + 7,3,0,0,849,147,1,0,0,0,850,851,5,41,0,0,851,149,1,0,0,0,852,853,7,5,0, + 0,853,854,7,6,0,0,854,855,7,19,0,0,855,856,7,3,0,0,856,151,1,0,0,0,857, + 858,5,61,0,0,858,859,5,61,0,0,859,153,1,0,0,0,860,861,5,61,0,0,861,862, + 5,126,0,0,862,155,1,0,0,0,863,864,5,33,0,0,864,865,5,61,0,0,865,157,1,0, + 0,0,866,867,5,60,0,0,867,159,1,0,0,0,868,869,5,60,0,0,869,870,5,61,0,0, + 870,161,1,0,0,0,871,872,5,62,0,0,872,163,1,0,0,0,873,874,5,62,0,0,874,875, + 5,61,0,0,875,165,1,0,0,0,876,877,5,43,0,0,877,167,1,0,0,0,878,879,5,45, + 0,0,879,169,1,0,0,0,880,881,5,42,0,0,881,171,1,0,0,0,882,883,5,47,0,0,883, + 173,1,0,0,0,884,885,5,37,0,0,885,175,1,0,0,0,886,887,3,144,64,0,887,891, + 3,82,33,0,888,890,3,98,41,0,889,888,1,0,0,0,890,893,1,0,0,0,891,889,1,0, + 0,0,891,892,1,0,0,0,892,901,1,0,0,0,893,891,1,0,0,0,894,896,3,144,64,0, + 895,897,3,80,32,0,896,895,1,0,0,0,897,898,1,0,0,0,898,896,1,0,0,0,898,899, + 1,0,0,0,899,901,1,0,0,0,900,886,1,0,0,0,900,894,1,0,0,0,901,177,1,0,0,0, + 902,903,5,91,0,0,903,904,1,0,0,0,904,905,6,81,0,0,905,906,6,81,0,0,906, + 179,1,0,0,0,907,908,5,93,0,0,908,909,1,0,0,0,909,910,6,82,15,0,910,911, + 6,82,15,0,911,181,1,0,0,0,912,916,3,82,33,0,913,915,3,98,41,0,914,913,1, + 0,0,0,915,918,1,0,0,0,916,914,1,0,0,0,916,917,1,0,0,0,917,929,1,0,0,0,918, + 916,1,0,0,0,919,922,3,96,40,0,920,922,3,90,37,0,921,919,1,0,0,0,921,920, + 1,0,0,0,922,924,1,0,0,0,923,925,3,98,41,0,924,923,1,0,0,0,925,926,1,0,0, + 0,926,924,1,0,0,0,926,927,1,0,0,0,927,929,1,0,0,0,928,912,1,0,0,0,928,921, + 1,0,0,0,929,183,1,0,0,0,930,932,3,92,38,0,931,933,3,94,39,0,932,931,1,0, + 0,0,933,934,1,0,0,0,934,932,1,0,0,0,934,935,1,0,0,0,935,936,1,0,0,0,936, + 937,3,92,38,0,937,185,1,0,0,0,938,939,3,184,84,0,939,187,1,0,0,0,940,941, + 3,58,21,0,941,942,1,0,0,0,942,943,6,86,11,0,943,189,1,0,0,0,944,945,3,60, + 22,0,945,946,1,0,0,0,946,947,6,87,11,0,947,191,1,0,0,0,948,949,3,62,23, + 0,949,950,1,0,0,0,950,951,6,88,11,0,951,193,1,0,0,0,952,953,3,78,31,0,953, + 954,1,0,0,0,954,955,6,89,14,0,955,956,6,89,15,0,956,195,1,0,0,0,957,958, + 3,178,81,0,958,959,1,0,0,0,959,960,6,90,12,0,960,197,1,0,0,0,961,962,3, + 180,82,0,962,963,1,0,0,0,963,964,6,91,16,0,964,199,1,0,0,0,965,966,3,364, + 174,0,966,967,1,0,0,0,967,968,6,92,17,0,968,201,1,0,0,0,969,970,3,116,50, + 0,970,971,1,0,0,0,971,972,6,93,18,0,972,203,1,0,0,0,973,974,3,112,48,0, + 974,975,1,0,0,0,975,976,6,94,19,0,976,205,1,0,0,0,977,978,7,16,0,0,978, + 979,7,3,0,0,979,980,7,5,0,0,980,981,7,12,0,0,981,982,7,0,0,0,982,983,7, + 12,0,0,983,984,7,5,0,0,984,985,7,12,0,0,985,207,1,0,0,0,986,987,3,66,25, + 0,987,988,1,0,0,0,988,989,6,96,20,0,989,209,1,0,0,0,990,991,3,100,42,0, + 991,992,1,0,0,0,992,993,6,97,21,0,993,211,1,0,0,0,994,995,3,58,21,0,995, + 996,1,0,0,0,996,997,6,98,11,0,997,213,1,0,0,0,998,999,3,60,22,0,999,1000, + 1,0,0,0,1000,1001,6,99,11,0,1001,215,1,0,0,0,1002,1003,3,62,23,0,1003,1004, + 1,0,0,0,1004,1005,6,100,11,0,1005,217,1,0,0,0,1006,1007,3,78,31,0,1007, + 1008,1,0,0,0,1008,1009,6,101,14,0,1009,1010,6,101,15,0,1010,219,1,0,0,0, + 1011,1012,3,120,52,0,1012,1013,1,0,0,0,1013,1014,6,102,22,0,1014,221,1, + 0,0,0,1015,1016,3,116,50,0,1016,1017,1,0,0,0,1017,1018,6,103,18,0,1018, + 223,1,0,0,0,1019,1024,3,82,33,0,1020,1024,3,80,32,0,1021,1024,3,96,40,0, + 1022,1024,3,170,77,0,1023,1019,1,0,0,0,1023,1020,1,0,0,0,1023,1021,1,0, + 0,0,1023,1022,1,0,0,0,1024,225,1,0,0,0,1025,1028,3,82,33,0,1026,1028,3, + 170,77,0,1027,1025,1,0,0,0,1027,1026,1,0,0,0,1028,1032,1,0,0,0,1029,1031, + 3,224,104,0,1030,1029,1,0,0,0,1031,1034,1,0,0,0,1032,1030,1,0,0,0,1032, + 1033,1,0,0,0,1033,1045,1,0,0,0,1034,1032,1,0,0,0,1035,1038,3,96,40,0,1036, + 1038,3,90,37,0,1037,1035,1,0,0,0,1037,1036,1,0,0,0,1038,1040,1,0,0,0,1039, + 1041,3,224,104,0,1040,1039,1,0,0,0,1041,1042,1,0,0,0,1042,1040,1,0,0,0, + 1042,1043,1,0,0,0,1043,1045,1,0,0,0,1044,1027,1,0,0,0,1044,1037,1,0,0,0, + 1045,227,1,0,0,0,1046,1049,3,226,105,0,1047,1049,3,184,84,0,1048,1046,1, + 0,0,0,1048,1047,1,0,0,0,1049,1050,1,0,0,0,1050,1048,1,0,0,0,1050,1051,1, + 0,0,0,1051,229,1,0,0,0,1052,1053,3,58,21,0,1053,1054,1,0,0,0,1054,1055, + 6,107,11,0,1055,231,1,0,0,0,1056,1057,3,60,22,0,1057,1058,1,0,0,0,1058, + 1059,6,108,11,0,1059,233,1,0,0,0,1060,1061,3,62,23,0,1061,1062,1,0,0,0, + 1062,1063,6,109,11,0,1063,235,1,0,0,0,1064,1065,3,78,31,0,1065,1066,1,0, + 0,0,1066,1067,6,110,14,0,1067,1068,6,110,15,0,1068,237,1,0,0,0,1069,1070, + 3,112,48,0,1070,1071,1,0,0,0,1071,1072,6,111,19,0,1072,239,1,0,0,0,1073, + 1074,3,116,50,0,1074,1075,1,0,0,0,1075,1076,6,112,18,0,1076,241,1,0,0,0, + 1077,1078,3,120,52,0,1078,1079,1,0,0,0,1079,1080,6,113,22,0,1080,243,1, + 0,0,0,1081,1082,7,12,0,0,1082,1083,7,2,0,0,1083,245,1,0,0,0,1084,1085,3, + 228,106,0,1085,1086,1,0,0,0,1086,1087,6,115,23,0,1087,247,1,0,0,0,1088, + 1089,3,58,21,0,1089,1090,1,0,0,0,1090,1091,6,116,11,0,1091,249,1,0,0,0, + 1092,1093,3,60,22,0,1093,1094,1,0,0,0,1094,1095,6,117,11,0,1095,251,1,0, + 0,0,1096,1097,3,62,23,0,1097,1098,1,0,0,0,1098,1099,6,118,11,0,1099,253, + 1,0,0,0,1100,1101,3,78,31,0,1101,1102,1,0,0,0,1102,1103,6,119,14,0,1103, + 1104,6,119,15,0,1104,255,1,0,0,0,1105,1106,3,178,81,0,1106,1107,1,0,0,0, + 1107,1108,6,120,12,0,1108,1109,6,120,24,0,1109,257,1,0,0,0,1110,1111,7, + 7,0,0,1111,1112,7,9,0,0,1112,1113,1,0,0,0,1113,1114,6,121,25,0,1114,259, + 1,0,0,0,1115,1116,7,20,0,0,1116,1117,7,1,0,0,1117,1118,7,5,0,0,1118,1119, + 7,10,0,0,1119,1120,1,0,0,0,1120,1121,6,122,25,0,1121,261,1,0,0,0,1122,1123, + 8,34,0,0,1123,263,1,0,0,0,1124,1126,3,262,123,0,1125,1124,1,0,0,0,1126, + 1127,1,0,0,0,1127,1125,1,0,0,0,1127,1128,1,0,0,0,1128,1129,1,0,0,0,1129, + 1130,3,364,174,0,1130,1132,1,0,0,0,1131,1125,1,0,0,0,1131,1132,1,0,0,0, + 1132,1134,1,0,0,0,1133,1135,3,262,123,0,1134,1133,1,0,0,0,1135,1136,1,0, + 0,0,1136,1134,1,0,0,0,1136,1137,1,0,0,0,1137,265,1,0,0,0,1138,1139,3,264, + 124,0,1139,1140,1,0,0,0,1140,1141,6,125,26,0,1141,267,1,0,0,0,1142,1143, + 3,58,21,0,1143,1144,1,0,0,0,1144,1145,6,126,11,0,1145,269,1,0,0,0,1146, + 1147,3,60,22,0,1147,1148,1,0,0,0,1148,1149,6,127,11,0,1149,271,1,0,0,0, + 1150,1151,3,62,23,0,1151,1152,1,0,0,0,1152,1153,6,128,11,0,1153,273,1,0, + 0,0,1154,1155,3,78,31,0,1155,1156,1,0,0,0,1156,1157,6,129,14,0,1157,1158, + 6,129,15,0,1158,1159,6,129,15,0,1159,275,1,0,0,0,1160,1161,3,112,48,0,1161, + 1162,1,0,0,0,1162,1163,6,130,19,0,1163,277,1,0,0,0,1164,1165,3,116,50,0, + 1165,1166,1,0,0,0,1166,1167,6,131,18,0,1167,279,1,0,0,0,1168,1169,3,120, + 52,0,1169,1170,1,0,0,0,1170,1171,6,132,22,0,1171,281,1,0,0,0,1172,1173, + 3,260,122,0,1173,1174,1,0,0,0,1174,1175,6,133,27,0,1175,283,1,0,0,0,1176, + 1177,3,228,106,0,1177,1178,1,0,0,0,1178,1179,6,134,23,0,1179,285,1,0,0, + 0,1180,1181,3,186,85,0,1181,1182,1,0,0,0,1182,1183,6,135,28,0,1183,287, + 1,0,0,0,1184,1185,3,58,21,0,1185,1186,1,0,0,0,1186,1187,6,136,11,0,1187, + 289,1,0,0,0,1188,1189,3,60,22,0,1189,1190,1,0,0,0,1190,1191,6,137,11,0, + 1191,291,1,0,0,0,1192,1193,3,62,23,0,1193,1194,1,0,0,0,1194,1195,6,138, + 11,0,1195,293,1,0,0,0,1196,1197,3,78,31,0,1197,1198,1,0,0,0,1198,1199,6, + 139,14,0,1199,1200,6,139,15,0,1200,295,1,0,0,0,1201,1202,3,364,174,0,1202, + 1203,1,0,0,0,1203,1204,6,140,17,0,1204,297,1,0,0,0,1205,1206,3,116,50,0, + 1206,1207,1,0,0,0,1207,1208,6,141,18,0,1208,299,1,0,0,0,1209,1210,3,120, + 52,0,1210,1211,1,0,0,0,1211,1212,6,142,22,0,1212,301,1,0,0,0,1213,1214, + 3,258,121,0,1214,1215,1,0,0,0,1215,1216,6,143,29,0,1216,1217,6,143,30,0, + 1217,303,1,0,0,0,1218,1219,3,66,25,0,1219,1220,1,0,0,0,1220,1221,6,144, + 20,0,1221,305,1,0,0,0,1222,1223,3,100,42,0,1223,1224,1,0,0,0,1224,1225, + 6,145,21,0,1225,307,1,0,0,0,1226,1227,3,58,21,0,1227,1228,1,0,0,0,1228, + 1229,6,146,11,0,1229,309,1,0,0,0,1230,1231,3,60,22,0,1231,1232,1,0,0,0, + 1232,1233,6,147,11,0,1233,311,1,0,0,0,1234,1235,3,62,23,0,1235,1236,1,0, + 0,0,1236,1237,6,148,11,0,1237,313,1,0,0,0,1238,1239,3,78,31,0,1239,1240, + 1,0,0,0,1240,1241,6,149,14,0,1241,1242,6,149,15,0,1242,1243,6,149,15,0, + 1243,315,1,0,0,0,1244,1245,3,116,50,0,1245,1246,1,0,0,0,1246,1247,6,150, + 18,0,1247,317,1,0,0,0,1248,1249,3,120,52,0,1249,1250,1,0,0,0,1250,1251, + 6,151,22,0,1251,319,1,0,0,0,1252,1253,3,228,106,0,1253,1254,1,0,0,0,1254, + 1255,6,152,23,0,1255,321,1,0,0,0,1256,1257,3,58,21,0,1257,1258,1,0,0,0, + 1258,1259,6,153,11,0,1259,323,1,0,0,0,1260,1261,3,60,22,0,1261,1262,1,0, + 0,0,1262,1263,6,154,11,0,1263,325,1,0,0,0,1264,1265,3,62,23,0,1265,1266, + 1,0,0,0,1266,1267,6,155,11,0,1267,327,1,0,0,0,1268,1269,3,78,31,0,1269, + 1270,1,0,0,0,1270,1271,6,156,14,0,1271,1272,6,156,15,0,1272,329,1,0,0,0, + 1273,1274,3,120,52,0,1274,1275,1,0,0,0,1275,1276,6,157,22,0,1276,331,1, + 0,0,0,1277,1278,3,186,85,0,1278,1279,1,0,0,0,1279,1280,6,158,28,0,1280, + 333,1,0,0,0,1281,1282,3,182,83,0,1282,1283,1,0,0,0,1283,1284,6,159,31,0, + 1284,335,1,0,0,0,1285,1286,3,58,21,0,1286,1287,1,0,0,0,1287,1288,6,160, + 11,0,1288,337,1,0,0,0,1289,1290,3,60,22,0,1290,1291,1,0,0,0,1291,1292,6, + 161,11,0,1292,339,1,0,0,0,1293,1294,3,62,23,0,1294,1295,1,0,0,0,1295,1296, + 6,162,11,0,1296,341,1,0,0,0,1297,1298,3,78,31,0,1298,1299,1,0,0,0,1299, + 1300,6,163,14,0,1300,1301,6,163,15,0,1301,343,1,0,0,0,1302,1303,7,1,0,0, + 1303,1304,7,9,0,0,1304,1305,7,15,0,0,1305,1306,7,7,0,0,1306,345,1,0,0,0, + 1307,1308,3,58,21,0,1308,1309,1,0,0,0,1309,1310,6,165,11,0,1310,347,1,0, + 0,0,1311,1312,3,60,22,0,1312,1313,1,0,0,0,1313,1314,6,166,11,0,1314,349, + 1,0,0,0,1315,1316,3,62,23,0,1316,1317,1,0,0,0,1317,1318,6,167,11,0,1318, + 351,1,0,0,0,1319,1320,3,78,31,0,1320,1321,1,0,0,0,1321,1322,6,168,14,0, + 1322,1323,6,168,15,0,1323,353,1,0,0,0,1324,1325,7,15,0,0,1325,1326,7,19, + 0,0,1326,1327,7,9,0,0,1327,1328,7,4,0,0,1328,1329,7,5,0,0,1329,1330,7,1, + 0,0,1330,1331,7,7,0,0,1331,1332,7,9,0,0,1332,1333,7,2,0,0,1333,355,1,0, + 0,0,1334,1335,3,58,21,0,1335,1336,1,0,0,0,1336,1337,6,170,11,0,1337,357, + 1,0,0,0,1338,1339,3,60,22,0,1339,1340,1,0,0,0,1340,1341,6,171,11,0,1341, + 359,1,0,0,0,1342,1343,3,62,23,0,1343,1344,1,0,0,0,1344,1345,6,172,11,0, + 1345,361,1,0,0,0,1346,1347,3,180,82,0,1347,1348,1,0,0,0,1348,1349,6,173, + 16,0,1349,1350,6,173,15,0,1350,363,1,0,0,0,1351,1352,5,58,0,0,1352,365, + 1,0,0,0,1353,1359,3,90,37,0,1354,1359,3,80,32,0,1355,1359,3,120,52,0,1356, + 1359,3,82,33,0,1357,1359,3,96,40,0,1358,1353,1,0,0,0,1358,1354,1,0,0,0, + 1358,1355,1,0,0,0,1358,1356,1,0,0,0,1358,1357,1,0,0,0,1359,1360,1,0,0,0, + 1360,1358,1,0,0,0,1360,1361,1,0,0,0,1361,367,1,0,0,0,1362,1363,3,58,21, + 0,1363,1364,1,0,0,0,1364,1365,6,176,11,0,1365,369,1,0,0,0,1366,1367,3,60, + 22,0,1367,1368,1,0,0,0,1368,1369,6,177,11,0,1369,371,1,0,0,0,1370,1371, + 3,62,23,0,1371,1372,1,0,0,0,1372,1373,6,178,11,0,1373,373,1,0,0,0,1374, + 1375,3,78,31,0,1375,1376,1,0,0,0,1376,1377,6,179,14,0,1377,1378,6,179,15, + 0,1378,375,1,0,0,0,1379,1380,3,66,25,0,1380,1381,1,0,0,0,1381,1382,6,180, + 20,0,1382,1383,6,180,15,0,1383,1384,6,180,32,0,1384,377,1,0,0,0,1385,1386, + 3,100,42,0,1386,1387,1,0,0,0,1387,1388,6,181,21,0,1388,1389,6,181,15,0, + 1389,1390,6,181,32,0,1390,379,1,0,0,0,1391,1392,3,58,21,0,1392,1393,1,0, + 0,0,1393,1394,6,182,11,0,1394,381,1,0,0,0,1395,1396,3,60,22,0,1396,1397, + 1,0,0,0,1397,1398,6,183,11,0,1398,383,1,0,0,0,1399,1400,3,62,23,0,1400, + 1401,1,0,0,0,1401,1402,6,184,11,0,1402,385,1,0,0,0,1403,1404,3,364,174, + 0,1404,1405,1,0,0,0,1405,1406,6,185,17,0,1406,1407,6,185,15,0,1407,1408, + 6,185,7,0,1408,387,1,0,0,0,1409,1410,3,116,50,0,1410,1411,1,0,0,0,1411, + 1412,6,186,18,0,1412,1413,6,186,15,0,1413,1414,6,186,7,0,1414,389,1,0,0, + 0,1415,1416,3,58,21,0,1416,1417,1,0,0,0,1417,1418,6,187,11,0,1418,391,1, + 0,0,0,1419,1420,3,60,22,0,1420,1421,1,0,0,0,1421,1422,6,188,11,0,1422,393, + 1,0,0,0,1423,1424,3,62,23,0,1424,1425,1,0,0,0,1425,1426,6,189,11,0,1426, + 395,1,0,0,0,1427,1428,3,186,85,0,1428,1429,1,0,0,0,1429,1430,6,190,15,0, + 1430,1431,6,190,0,0,1431,1432,6,190,28,0,1432,397,1,0,0,0,1433,1434,3,182, + 83,0,1434,1435,1,0,0,0,1435,1436,6,191,15,0,1436,1437,6,191,0,0,1437,1438, + 6,191,31,0,1438,399,1,0,0,0,1439,1440,3,106,45,0,1440,1441,1,0,0,0,1441, + 1442,6,192,15,0,1442,1443,6,192,0,0,1443,1444,6,192,33,0,1444,401,1,0,0, + 0,1445,1446,3,78,31,0,1446,1447,1,0,0,0,1447,1448,6,193,14,0,1448,1449, + 6,193,15,0,1449,403,1,0,0,0,65,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,576, + 586,590,593,602,604,615,622,627,666,671,680,687,692,694,705,713,716,718, + 723,728,734,741,746,752,755,763,767,891,898,900,916,921,926,928,934,1023, + 1027,1032,1037,1042,1044,1048,1050,1127,1131,1136,1358,1360,34,5,2,0,5, 4,0,5,6,0,5,1,0,5,3,0,5,8,0,5,12,0,5,14,0,5,10,0,5,5,0,5,11,0,0,1,0,7,69, - 0,5,0,0,7,29,0,4,0,0,7,70,0,7,38,0,7,36,0,7,30,0,7,25,0,7,40,0,7,80,0,5, - 13,0,5,7,0,7,72,0,7,90,0,7,89,0,7,88,0,5,9,0,7,71,0,5,15,0,7,33,0]; + 0,5,0,0,7,29,0,4,0,0,7,70,0,7,114,0,7,38,0,7,36,0,7,25,0,7,30,0,7,40,0, + 7,80,0,5,13,0,5,7,0,7,90,0,7,89,0,7,72,0,7,88,0,5,9,0,7,71,0,5,15,0,7,33, + 0]; private static __ATN: ATN; public static get _ATN(): ATN { diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 index 59c9ee6ca5770..bae91675fbb02 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 @@ -115,11 +115,21 @@ field ; fromCommand - : FROM indexIdentifier (COMMA indexIdentifier)* metadata? + : FROM indexPattern (COMMA indexPattern)* metadata? ; -indexIdentifier - : INDEX_UNQUOTED_IDENTIFIER +indexPattern + : clusterString COLON indexString + | indexString + ; + +clusterString + : UNQUOTED_SOURCE + ; + +indexString + : UNQUOTED_SOURCE + | QUOTED_STRING ; metadata @@ -128,7 +138,7 @@ metadata ; metadataOption - : METADATA indexIdentifier (COMMA indexIdentifier)* + : METADATA UNQUOTED_SOURCE (COMMA UNQUOTED_SOURCE)* ; deprecated_metadata @@ -136,7 +146,7 @@ deprecated_metadata ; metricsCommand - : METRICS indexIdentifier (COMMA indexIdentifier)* aggregates=fields? (BY grouping=fields)? + : METRICS indexPattern (COMMA indexPattern)* aggregates=fields? (BY grouping=fields)? ; evalCommand @@ -289,5 +299,5 @@ enrichWithClause ; lookupCommand - : LOOKUP tableName=INDEX_UNQUOTED_IDENTIFIER ON matchFields=qualifiedNamePatterns + : LOOKUP tableName=indexPattern ON matchFields=qualifiedNamePatterns ; \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.interp b/packages/kbn-esql-ast/src/antlr/esql_parser.interp index 5900020590110..6c5edef9e98f0 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.interp @@ -151,7 +151,7 @@ UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS -INDEX_UNQUOTED_IDENTIFIER +UNQUOTED_SOURCE EXPLAIN_WS EXPLAIN_LINE_COMMENT EXPLAIN_MULTILINE_COMMENT @@ -269,7 +269,9 @@ rowCommand fields field fromCommand -indexIdentifier +indexPattern +clusterString +indexString metadata metadataOption deprecated_metadata @@ -312,4 +314,4 @@ lookupCommand atn: -[4, 1, 124, 554, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 122, 8, 1, 10, 1, 12, 1, 125, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 133, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 149, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 161, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 168, 8, 5, 10, 5, 12, 5, 171, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 178, 8, 5, 1, 5, 1, 5, 3, 5, 182, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 190, 8, 5, 10, 5, 12, 5, 193, 9, 5, 1, 6, 1, 6, 3, 6, 197, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 204, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 209, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 216, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 222, 8, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 230, 8, 8, 10, 8, 12, 8, 233, 9, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 243, 8, 9, 1, 9, 1, 9, 1, 9, 5, 9, 248, 8, 9, 10, 9, 12, 9, 251, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 259, 8, 10, 10, 10, 12, 10, 262, 9, 10, 3, 10, 264, 8, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 276, 8, 13, 10, 13, 12, 13, 279, 9, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 286, 8, 14, 1, 15, 1, 15, 1, 15, 1, 15, 5, 15, 292, 8, 15, 10, 15, 12, 15, 295, 9, 15, 1, 15, 3, 15, 298, 8, 15, 1, 16, 1, 16, 1, 17, 1, 17, 3, 17, 304, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 5, 18, 310, 8, 18, 10, 18, 12, 18, 313, 9, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 323, 8, 20, 10, 20, 12, 20, 326, 9, 20, 1, 20, 3, 20, 329, 8, 20, 1, 20, 1, 20, 3, 20, 333, 8, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 3, 22, 340, 8, 22, 1, 22, 1, 22, 3, 22, 344, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 350, 8, 23, 1, 24, 1, 24, 1, 24, 5, 24, 355, 8, 24, 10, 24, 12, 24, 358, 9, 24, 1, 25, 1, 25, 1, 25, 5, 25, 363, 8, 25, 10, 25, 12, 25, 366, 9, 25, 1, 26, 1, 26, 1, 26, 5, 26, 371, 8, 26, 10, 26, 12, 26, 374, 9, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 393, 8, 29, 10, 29, 12, 29, 396, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 404, 8, 29, 10, 29, 12, 29, 407, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 415, 8, 29, 10, 29, 12, 29, 418, 9, 29, 1, 29, 1, 29, 3, 29, 422, 8, 29, 1, 30, 1, 30, 3, 30, 426, 8, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 5, 32, 435, 8, 32, 10, 32, 12, 32, 438, 9, 32, 1, 33, 1, 33, 3, 33, 442, 8, 33, 1, 33, 1, 33, 3, 33, 446, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 5, 36, 458, 8, 36, 10, 36, 12, 36, 461, 9, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 471, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 5, 41, 483, 8, 41, 10, 41, 12, 41, 486, 9, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 3, 44, 496, 8, 44, 1, 45, 3, 45, 499, 8, 45, 1, 45, 1, 45, 1, 46, 3, 46, 504, 8, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 529, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 5, 53, 535, 8, 53, 10, 53, 12, 53, 538, 9, 53, 3, 53, 540, 8, 53, 1, 54, 1, 54, 1, 54, 3, 54, 545, 8, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 0, 4, 2, 10, 16, 18, 56, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 0, 7, 1, 0, 63, 64, 1, 0, 65, 67, 1, 0, 71, 72, 2, 0, 35, 35, 39, 39, 1, 0, 42, 43, 2, 0, 41, 41, 55, 55, 2, 0, 56, 56, 58, 62, 580, 0, 112, 1, 0, 0, 0, 2, 115, 1, 0, 0, 0, 4, 132, 1, 0, 0, 0, 6, 148, 1, 0, 0, 0, 8, 150, 1, 0, 0, 0, 10, 181, 1, 0, 0, 0, 12, 208, 1, 0, 0, 0, 14, 215, 1, 0, 0, 0, 16, 221, 1, 0, 0, 0, 18, 242, 1, 0, 0, 0, 20, 252, 1, 0, 0, 0, 22, 267, 1, 0, 0, 0, 24, 269, 1, 0, 0, 0, 26, 272, 1, 0, 0, 0, 28, 285, 1, 0, 0, 0, 30, 287, 1, 0, 0, 0, 32, 299, 1, 0, 0, 0, 34, 303, 1, 0, 0, 0, 36, 305, 1, 0, 0, 0, 38, 314, 1, 0, 0, 0, 40, 318, 1, 0, 0, 0, 42, 334, 1, 0, 0, 0, 44, 337, 1, 0, 0, 0, 46, 345, 1, 0, 0, 0, 48, 351, 1, 0, 0, 0, 50, 359, 1, 0, 0, 0, 52, 367, 1, 0, 0, 0, 54, 375, 1, 0, 0, 0, 56, 377, 1, 0, 0, 0, 58, 421, 1, 0, 0, 0, 60, 425, 1, 0, 0, 0, 62, 427, 1, 0, 0, 0, 64, 430, 1, 0, 0, 0, 66, 439, 1, 0, 0, 0, 68, 447, 1, 0, 0, 0, 70, 450, 1, 0, 0, 0, 72, 453, 1, 0, 0, 0, 74, 462, 1, 0, 0, 0, 76, 466, 1, 0, 0, 0, 78, 472, 1, 0, 0, 0, 80, 476, 1, 0, 0, 0, 82, 479, 1, 0, 0, 0, 84, 487, 1, 0, 0, 0, 86, 491, 1, 0, 0, 0, 88, 495, 1, 0, 0, 0, 90, 498, 1, 0, 0, 0, 92, 503, 1, 0, 0, 0, 94, 507, 1, 0, 0, 0, 96, 509, 1, 0, 0, 0, 98, 511, 1, 0, 0, 0, 100, 514, 1, 0, 0, 0, 102, 518, 1, 0, 0, 0, 104, 521, 1, 0, 0, 0, 106, 524, 1, 0, 0, 0, 108, 544, 1, 0, 0, 0, 110, 548, 1, 0, 0, 0, 112, 113, 3, 2, 1, 0, 113, 114, 5, 0, 0, 1, 114, 1, 1, 0, 0, 0, 115, 116, 6, 1, -1, 0, 116, 117, 3, 4, 2, 0, 117, 123, 1, 0, 0, 0, 118, 119, 10, 1, 0, 0, 119, 120, 5, 29, 0, 0, 120, 122, 3, 6, 3, 0, 121, 118, 1, 0, 0, 0, 122, 125, 1, 0, 0, 0, 123, 121, 1, 0, 0, 0, 123, 124, 1, 0, 0, 0, 124, 3, 1, 0, 0, 0, 125, 123, 1, 0, 0, 0, 126, 133, 3, 98, 49, 0, 127, 133, 3, 30, 15, 0, 128, 133, 3, 24, 12, 0, 129, 133, 3, 40, 20, 0, 130, 133, 3, 102, 51, 0, 131, 133, 3, 104, 52, 0, 132, 126, 1, 0, 0, 0, 132, 127, 1, 0, 0, 0, 132, 128, 1, 0, 0, 0, 132, 129, 1, 0, 0, 0, 132, 130, 1, 0, 0, 0, 132, 131, 1, 0, 0, 0, 133, 5, 1, 0, 0, 0, 134, 149, 3, 42, 21, 0, 135, 149, 3, 46, 23, 0, 136, 149, 3, 62, 31, 0, 137, 149, 3, 110, 55, 0, 138, 149, 3, 68, 34, 0, 139, 149, 3, 64, 32, 0, 140, 149, 3, 44, 22, 0, 141, 149, 3, 8, 4, 0, 142, 149, 3, 70, 35, 0, 143, 149, 3, 72, 36, 0, 144, 149, 3, 76, 38, 0, 145, 149, 3, 78, 39, 0, 146, 149, 3, 106, 53, 0, 147, 149, 3, 80, 40, 0, 148, 134, 1, 0, 0, 0, 148, 135, 1, 0, 0, 0, 148, 136, 1, 0, 0, 0, 148, 137, 1, 0, 0, 0, 148, 138, 1, 0, 0, 0, 148, 139, 1, 0, 0, 0, 148, 140, 1, 0, 0, 0, 148, 141, 1, 0, 0, 0, 148, 142, 1, 0, 0, 0, 148, 143, 1, 0, 0, 0, 148, 144, 1, 0, 0, 0, 148, 145, 1, 0, 0, 0, 148, 146, 1, 0, 0, 0, 148, 147, 1, 0, 0, 0, 149, 7, 1, 0, 0, 0, 150, 151, 5, 20, 0, 0, 151, 152, 3, 10, 5, 0, 152, 9, 1, 0, 0, 0, 153, 154, 6, 5, -1, 0, 154, 155, 5, 48, 0, 0, 155, 182, 3, 10, 5, 7, 156, 182, 3, 14, 7, 0, 157, 182, 3, 12, 6, 0, 158, 160, 3, 14, 7, 0, 159, 161, 5, 48, 0, 0, 160, 159, 1, 0, 0, 0, 160, 161, 1, 0, 0, 0, 161, 162, 1, 0, 0, 0, 162, 163, 5, 45, 0, 0, 163, 164, 5, 44, 0, 0, 164, 169, 3, 14, 7, 0, 165, 166, 5, 38, 0, 0, 166, 168, 3, 14, 7, 0, 167, 165, 1, 0, 0, 0, 168, 171, 1, 0, 0, 0, 169, 167, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 172, 1, 0, 0, 0, 171, 169, 1, 0, 0, 0, 172, 173, 5, 54, 0, 0, 173, 182, 1, 0, 0, 0, 174, 175, 3, 14, 7, 0, 175, 177, 5, 46, 0, 0, 176, 178, 5, 48, 0, 0, 177, 176, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 179, 1, 0, 0, 0, 179, 180, 5, 49, 0, 0, 180, 182, 1, 0, 0, 0, 181, 153, 1, 0, 0, 0, 181, 156, 1, 0, 0, 0, 181, 157, 1, 0, 0, 0, 181, 158, 1, 0, 0, 0, 181, 174, 1, 0, 0, 0, 182, 191, 1, 0, 0, 0, 183, 184, 10, 4, 0, 0, 184, 185, 5, 34, 0, 0, 185, 190, 3, 10, 5, 5, 186, 187, 10, 3, 0, 0, 187, 188, 5, 51, 0, 0, 188, 190, 3, 10, 5, 4, 189, 183, 1, 0, 0, 0, 189, 186, 1, 0, 0, 0, 190, 193, 1, 0, 0, 0, 191, 189, 1, 0, 0, 0, 191, 192, 1, 0, 0, 0, 192, 11, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 194, 196, 3, 14, 7, 0, 195, 197, 5, 48, 0, 0, 196, 195, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 198, 1, 0, 0, 0, 198, 199, 5, 47, 0, 0, 199, 200, 3, 94, 47, 0, 200, 209, 1, 0, 0, 0, 201, 203, 3, 14, 7, 0, 202, 204, 5, 48, 0, 0, 203, 202, 1, 0, 0, 0, 203, 204, 1, 0, 0, 0, 204, 205, 1, 0, 0, 0, 205, 206, 5, 53, 0, 0, 206, 207, 3, 94, 47, 0, 207, 209, 1, 0, 0, 0, 208, 194, 1, 0, 0, 0, 208, 201, 1, 0, 0, 0, 209, 13, 1, 0, 0, 0, 210, 216, 3, 16, 8, 0, 211, 212, 3, 16, 8, 0, 212, 213, 3, 96, 48, 0, 213, 214, 3, 16, 8, 0, 214, 216, 1, 0, 0, 0, 215, 210, 1, 0, 0, 0, 215, 211, 1, 0, 0, 0, 216, 15, 1, 0, 0, 0, 217, 218, 6, 8, -1, 0, 218, 222, 3, 18, 9, 0, 219, 220, 7, 0, 0, 0, 220, 222, 3, 16, 8, 3, 221, 217, 1, 0, 0, 0, 221, 219, 1, 0, 0, 0, 222, 231, 1, 0, 0, 0, 223, 224, 10, 2, 0, 0, 224, 225, 7, 1, 0, 0, 225, 230, 3, 16, 8, 3, 226, 227, 10, 1, 0, 0, 227, 228, 7, 0, 0, 0, 228, 230, 3, 16, 8, 2, 229, 223, 1, 0, 0, 0, 229, 226, 1, 0, 0, 0, 230, 233, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 231, 232, 1, 0, 0, 0, 232, 17, 1, 0, 0, 0, 233, 231, 1, 0, 0, 0, 234, 235, 6, 9, -1, 0, 235, 243, 3, 58, 29, 0, 236, 243, 3, 48, 24, 0, 237, 243, 3, 20, 10, 0, 238, 239, 5, 44, 0, 0, 239, 240, 3, 10, 5, 0, 240, 241, 5, 54, 0, 0, 241, 243, 1, 0, 0, 0, 242, 234, 1, 0, 0, 0, 242, 236, 1, 0, 0, 0, 242, 237, 1, 0, 0, 0, 242, 238, 1, 0, 0, 0, 243, 249, 1, 0, 0, 0, 244, 245, 10, 1, 0, 0, 245, 246, 5, 37, 0, 0, 246, 248, 3, 22, 11, 0, 247, 244, 1, 0, 0, 0, 248, 251, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 19, 1, 0, 0, 0, 251, 249, 1, 0, 0, 0, 252, 253, 3, 54, 27, 0, 253, 263, 5, 44, 0, 0, 254, 264, 5, 65, 0, 0, 255, 260, 3, 10, 5, 0, 256, 257, 5, 38, 0, 0, 257, 259, 3, 10, 5, 0, 258, 256, 1, 0, 0, 0, 259, 262, 1, 0, 0, 0, 260, 258, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 264, 1, 0, 0, 0, 262, 260, 1, 0, 0, 0, 263, 254, 1, 0, 0, 0, 263, 255, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 266, 5, 54, 0, 0, 266, 21, 1, 0, 0, 0, 267, 268, 3, 54, 27, 0, 268, 23, 1, 0, 0, 0, 269, 270, 5, 16, 0, 0, 270, 271, 3, 26, 13, 0, 271, 25, 1, 0, 0, 0, 272, 277, 3, 28, 14, 0, 273, 274, 5, 38, 0, 0, 274, 276, 3, 28, 14, 0, 275, 273, 1, 0, 0, 0, 276, 279, 1, 0, 0, 0, 277, 275, 1, 0, 0, 0, 277, 278, 1, 0, 0, 0, 278, 27, 1, 0, 0, 0, 279, 277, 1, 0, 0, 0, 280, 286, 3, 10, 5, 0, 281, 282, 3, 48, 24, 0, 282, 283, 5, 36, 0, 0, 283, 284, 3, 10, 5, 0, 284, 286, 1, 0, 0, 0, 285, 280, 1, 0, 0, 0, 285, 281, 1, 0, 0, 0, 286, 29, 1, 0, 0, 0, 287, 288, 5, 6, 0, 0, 288, 293, 3, 32, 16, 0, 289, 290, 5, 38, 0, 0, 290, 292, 3, 32, 16, 0, 291, 289, 1, 0, 0, 0, 292, 295, 1, 0, 0, 0, 293, 291, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 297, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 296, 298, 3, 34, 17, 0, 297, 296, 1, 0, 0, 0, 297, 298, 1, 0, 0, 0, 298, 31, 1, 0, 0, 0, 299, 300, 5, 25, 0, 0, 300, 33, 1, 0, 0, 0, 301, 304, 3, 36, 18, 0, 302, 304, 3, 38, 19, 0, 303, 301, 1, 0, 0, 0, 303, 302, 1, 0, 0, 0, 304, 35, 1, 0, 0, 0, 305, 306, 5, 76, 0, 0, 306, 311, 3, 32, 16, 0, 307, 308, 5, 38, 0, 0, 308, 310, 3, 32, 16, 0, 309, 307, 1, 0, 0, 0, 310, 313, 1, 0, 0, 0, 311, 309, 1, 0, 0, 0, 311, 312, 1, 0, 0, 0, 312, 37, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 314, 315, 5, 69, 0, 0, 315, 316, 3, 36, 18, 0, 316, 317, 5, 70, 0, 0, 317, 39, 1, 0, 0, 0, 318, 319, 5, 13, 0, 0, 319, 324, 3, 32, 16, 0, 320, 321, 5, 38, 0, 0, 321, 323, 3, 32, 16, 0, 322, 320, 1, 0, 0, 0, 323, 326, 1, 0, 0, 0, 324, 322, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 328, 1, 0, 0, 0, 326, 324, 1, 0, 0, 0, 327, 329, 3, 26, 13, 0, 328, 327, 1, 0, 0, 0, 328, 329, 1, 0, 0, 0, 329, 332, 1, 0, 0, 0, 330, 331, 5, 33, 0, 0, 331, 333, 3, 26, 13, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 41, 1, 0, 0, 0, 334, 335, 5, 4, 0, 0, 335, 336, 3, 26, 13, 0, 336, 43, 1, 0, 0, 0, 337, 339, 5, 19, 0, 0, 338, 340, 3, 26, 13, 0, 339, 338, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 343, 1, 0, 0, 0, 341, 342, 5, 33, 0, 0, 342, 344, 3, 26, 13, 0, 343, 341, 1, 0, 0, 0, 343, 344, 1, 0, 0, 0, 344, 45, 1, 0, 0, 0, 345, 346, 5, 8, 0, 0, 346, 349, 3, 26, 13, 0, 347, 348, 5, 33, 0, 0, 348, 350, 3, 26, 13, 0, 349, 347, 1, 0, 0, 0, 349, 350, 1, 0, 0, 0, 350, 47, 1, 0, 0, 0, 351, 356, 3, 54, 27, 0, 352, 353, 5, 40, 0, 0, 353, 355, 3, 54, 27, 0, 354, 352, 1, 0, 0, 0, 355, 358, 1, 0, 0, 0, 356, 354, 1, 0, 0, 0, 356, 357, 1, 0, 0, 0, 357, 49, 1, 0, 0, 0, 358, 356, 1, 0, 0, 0, 359, 364, 3, 56, 28, 0, 360, 361, 5, 40, 0, 0, 361, 363, 3, 56, 28, 0, 362, 360, 1, 0, 0, 0, 363, 366, 1, 0, 0, 0, 364, 362, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 51, 1, 0, 0, 0, 366, 364, 1, 0, 0, 0, 367, 372, 3, 50, 25, 0, 368, 369, 5, 38, 0, 0, 369, 371, 3, 50, 25, 0, 370, 368, 1, 0, 0, 0, 371, 374, 1, 0, 0, 0, 372, 370, 1, 0, 0, 0, 372, 373, 1, 0, 0, 0, 373, 53, 1, 0, 0, 0, 374, 372, 1, 0, 0, 0, 375, 376, 7, 2, 0, 0, 376, 55, 1, 0, 0, 0, 377, 378, 5, 80, 0, 0, 378, 57, 1, 0, 0, 0, 379, 422, 5, 49, 0, 0, 380, 381, 3, 92, 46, 0, 381, 382, 5, 71, 0, 0, 382, 422, 1, 0, 0, 0, 383, 422, 3, 90, 45, 0, 384, 422, 3, 92, 46, 0, 385, 422, 3, 86, 43, 0, 386, 422, 3, 60, 30, 0, 387, 422, 3, 94, 47, 0, 388, 389, 5, 69, 0, 0, 389, 394, 3, 88, 44, 0, 390, 391, 5, 38, 0, 0, 391, 393, 3, 88, 44, 0, 392, 390, 1, 0, 0, 0, 393, 396, 1, 0, 0, 0, 394, 392, 1, 0, 0, 0, 394, 395, 1, 0, 0, 0, 395, 397, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 397, 398, 5, 70, 0, 0, 398, 422, 1, 0, 0, 0, 399, 400, 5, 69, 0, 0, 400, 405, 3, 86, 43, 0, 401, 402, 5, 38, 0, 0, 402, 404, 3, 86, 43, 0, 403, 401, 1, 0, 0, 0, 404, 407, 1, 0, 0, 0, 405, 403, 1, 0, 0, 0, 405, 406, 1, 0, 0, 0, 406, 408, 1, 0, 0, 0, 407, 405, 1, 0, 0, 0, 408, 409, 5, 70, 0, 0, 409, 422, 1, 0, 0, 0, 410, 411, 5, 69, 0, 0, 411, 416, 3, 94, 47, 0, 412, 413, 5, 38, 0, 0, 413, 415, 3, 94, 47, 0, 414, 412, 1, 0, 0, 0, 415, 418, 1, 0, 0, 0, 416, 414, 1, 0, 0, 0, 416, 417, 1, 0, 0, 0, 417, 419, 1, 0, 0, 0, 418, 416, 1, 0, 0, 0, 419, 420, 5, 70, 0, 0, 420, 422, 1, 0, 0, 0, 421, 379, 1, 0, 0, 0, 421, 380, 1, 0, 0, 0, 421, 383, 1, 0, 0, 0, 421, 384, 1, 0, 0, 0, 421, 385, 1, 0, 0, 0, 421, 386, 1, 0, 0, 0, 421, 387, 1, 0, 0, 0, 421, 388, 1, 0, 0, 0, 421, 399, 1, 0, 0, 0, 421, 410, 1, 0, 0, 0, 422, 59, 1, 0, 0, 0, 423, 426, 5, 52, 0, 0, 424, 426, 5, 68, 0, 0, 425, 423, 1, 0, 0, 0, 425, 424, 1, 0, 0, 0, 426, 61, 1, 0, 0, 0, 427, 428, 5, 10, 0, 0, 428, 429, 5, 31, 0, 0, 429, 63, 1, 0, 0, 0, 430, 431, 5, 18, 0, 0, 431, 436, 3, 66, 33, 0, 432, 433, 5, 38, 0, 0, 433, 435, 3, 66, 33, 0, 434, 432, 1, 0, 0, 0, 435, 438, 1, 0, 0, 0, 436, 434, 1, 0, 0, 0, 436, 437, 1, 0, 0, 0, 437, 65, 1, 0, 0, 0, 438, 436, 1, 0, 0, 0, 439, 441, 3, 10, 5, 0, 440, 442, 7, 3, 0, 0, 441, 440, 1, 0, 0, 0, 441, 442, 1, 0, 0, 0, 442, 445, 1, 0, 0, 0, 443, 444, 5, 50, 0, 0, 444, 446, 7, 4, 0, 0, 445, 443, 1, 0, 0, 0, 445, 446, 1, 0, 0, 0, 446, 67, 1, 0, 0, 0, 447, 448, 5, 9, 0, 0, 448, 449, 3, 52, 26, 0, 449, 69, 1, 0, 0, 0, 450, 451, 5, 2, 0, 0, 451, 452, 3, 52, 26, 0, 452, 71, 1, 0, 0, 0, 453, 454, 5, 15, 0, 0, 454, 459, 3, 74, 37, 0, 455, 456, 5, 38, 0, 0, 456, 458, 3, 74, 37, 0, 457, 455, 1, 0, 0, 0, 458, 461, 1, 0, 0, 0, 459, 457, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 73, 1, 0, 0, 0, 461, 459, 1, 0, 0, 0, 462, 463, 3, 50, 25, 0, 463, 464, 5, 84, 0, 0, 464, 465, 3, 50, 25, 0, 465, 75, 1, 0, 0, 0, 466, 467, 5, 1, 0, 0, 467, 468, 3, 18, 9, 0, 468, 470, 3, 94, 47, 0, 469, 471, 3, 82, 41, 0, 470, 469, 1, 0, 0, 0, 470, 471, 1, 0, 0, 0, 471, 77, 1, 0, 0, 0, 472, 473, 5, 7, 0, 0, 473, 474, 3, 18, 9, 0, 474, 475, 3, 94, 47, 0, 475, 79, 1, 0, 0, 0, 476, 477, 5, 14, 0, 0, 477, 478, 3, 48, 24, 0, 478, 81, 1, 0, 0, 0, 479, 484, 3, 84, 42, 0, 480, 481, 5, 38, 0, 0, 481, 483, 3, 84, 42, 0, 482, 480, 1, 0, 0, 0, 483, 486, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 83, 1, 0, 0, 0, 486, 484, 1, 0, 0, 0, 487, 488, 3, 54, 27, 0, 488, 489, 5, 36, 0, 0, 489, 490, 3, 58, 29, 0, 490, 85, 1, 0, 0, 0, 491, 492, 7, 5, 0, 0, 492, 87, 1, 0, 0, 0, 493, 496, 3, 90, 45, 0, 494, 496, 3, 92, 46, 0, 495, 493, 1, 0, 0, 0, 495, 494, 1, 0, 0, 0, 496, 89, 1, 0, 0, 0, 497, 499, 7, 0, 0, 0, 498, 497, 1, 0, 0, 0, 498, 499, 1, 0, 0, 0, 499, 500, 1, 0, 0, 0, 500, 501, 5, 32, 0, 0, 501, 91, 1, 0, 0, 0, 502, 504, 7, 0, 0, 0, 503, 502, 1, 0, 0, 0, 503, 504, 1, 0, 0, 0, 504, 505, 1, 0, 0, 0, 505, 506, 5, 31, 0, 0, 506, 93, 1, 0, 0, 0, 507, 508, 5, 30, 0, 0, 508, 95, 1, 0, 0, 0, 509, 510, 7, 6, 0, 0, 510, 97, 1, 0, 0, 0, 511, 512, 5, 5, 0, 0, 512, 513, 3, 100, 50, 0, 513, 99, 1, 0, 0, 0, 514, 515, 5, 69, 0, 0, 515, 516, 3, 2, 1, 0, 516, 517, 5, 70, 0, 0, 517, 101, 1, 0, 0, 0, 518, 519, 5, 17, 0, 0, 519, 520, 5, 106, 0, 0, 520, 103, 1, 0, 0, 0, 521, 522, 5, 12, 0, 0, 522, 523, 5, 110, 0, 0, 523, 105, 1, 0, 0, 0, 524, 525, 5, 3, 0, 0, 525, 528, 5, 90, 0, 0, 526, 527, 5, 88, 0, 0, 527, 529, 3, 50, 25, 0, 528, 526, 1, 0, 0, 0, 528, 529, 1, 0, 0, 0, 529, 539, 1, 0, 0, 0, 530, 531, 5, 89, 0, 0, 531, 536, 3, 108, 54, 0, 532, 533, 5, 38, 0, 0, 533, 535, 3, 108, 54, 0, 534, 532, 1, 0, 0, 0, 535, 538, 1, 0, 0, 0, 536, 534, 1, 0, 0, 0, 536, 537, 1, 0, 0, 0, 537, 540, 1, 0, 0, 0, 538, 536, 1, 0, 0, 0, 539, 530, 1, 0, 0, 0, 539, 540, 1, 0, 0, 0, 540, 107, 1, 0, 0, 0, 541, 542, 3, 50, 25, 0, 542, 543, 5, 36, 0, 0, 543, 545, 1, 0, 0, 0, 544, 541, 1, 0, 0, 0, 544, 545, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 547, 3, 50, 25, 0, 547, 109, 1, 0, 0, 0, 548, 549, 5, 11, 0, 0, 549, 550, 5, 25, 0, 0, 550, 551, 5, 88, 0, 0, 551, 552, 3, 52, 26, 0, 552, 111, 1, 0, 0, 0, 53, 123, 132, 148, 160, 169, 177, 181, 189, 191, 196, 203, 208, 215, 221, 229, 231, 242, 249, 260, 263, 277, 285, 293, 297, 303, 311, 324, 328, 332, 339, 343, 349, 356, 364, 372, 394, 405, 416, 421, 425, 436, 441, 445, 459, 470, 484, 495, 498, 503, 528, 536, 539, 544] \ No newline at end of file +[4, 1, 124, 567, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 126, 8, 1, 10, 1, 12, 1, 129, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 137, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 153, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 165, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 172, 8, 5, 10, 5, 12, 5, 175, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 182, 8, 5, 1, 5, 1, 5, 3, 5, 186, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 194, 8, 5, 10, 5, 12, 5, 197, 9, 5, 1, 6, 1, 6, 3, 6, 201, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 208, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 213, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 220, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 226, 8, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 234, 8, 8, 10, 8, 12, 8, 237, 9, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 247, 8, 9, 1, 9, 1, 9, 1, 9, 5, 9, 252, 8, 9, 10, 9, 12, 9, 255, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 263, 8, 10, 10, 10, 12, 10, 266, 9, 10, 3, 10, 268, 8, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 280, 8, 13, 10, 13, 12, 13, 283, 9, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 290, 8, 14, 1, 15, 1, 15, 1, 15, 1, 15, 5, 15, 296, 8, 15, 10, 15, 12, 15, 299, 9, 15, 1, 15, 3, 15, 302, 8, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 3, 16, 309, 8, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 3, 19, 317, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 323, 8, 20, 10, 20, 12, 20, 326, 9, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 5, 22, 336, 8, 22, 10, 22, 12, 22, 339, 9, 22, 1, 22, 3, 22, 342, 8, 22, 1, 22, 1, 22, 3, 22, 346, 8, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 3, 24, 353, 8, 24, 1, 24, 1, 24, 3, 24, 357, 8, 24, 1, 25, 1, 25, 1, 25, 1, 25, 3, 25, 363, 8, 25, 1, 26, 1, 26, 1, 26, 5, 26, 368, 8, 26, 10, 26, 12, 26, 371, 9, 26, 1, 27, 1, 27, 1, 27, 5, 27, 376, 8, 27, 10, 27, 12, 27, 379, 9, 27, 1, 28, 1, 28, 1, 28, 5, 28, 384, 8, 28, 10, 28, 12, 28, 387, 9, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 406, 8, 31, 10, 31, 12, 31, 409, 9, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 417, 8, 31, 10, 31, 12, 31, 420, 9, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 428, 8, 31, 10, 31, 12, 31, 431, 9, 31, 1, 31, 1, 31, 3, 31, 435, 8, 31, 1, 32, 1, 32, 3, 32, 439, 8, 32, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 448, 8, 34, 10, 34, 12, 34, 451, 9, 34, 1, 35, 1, 35, 3, 35, 455, 8, 35, 1, 35, 1, 35, 3, 35, 459, 8, 35, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 471, 8, 38, 10, 38, 12, 38, 474, 9, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 484, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 5, 43, 496, 8, 43, 10, 43, 12, 43, 499, 9, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 3, 46, 509, 8, 46, 1, 47, 3, 47, 512, 8, 47, 1, 47, 1, 47, 1, 48, 3, 48, 517, 8, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 542, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 5, 55, 548, 8, 55, 10, 55, 12, 55, 551, 9, 55, 3, 55, 553, 8, 55, 1, 56, 1, 56, 1, 56, 3, 56, 558, 8, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 0, 4, 2, 10, 16, 18, 58, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 0, 8, 1, 0, 63, 64, 1, 0, 65, 67, 2, 0, 25, 25, 30, 30, 1, 0, 71, 72, 2, 0, 35, 35, 39, 39, 1, 0, 42, 43, 2, 0, 41, 41, 55, 55, 2, 0, 56, 56, 58, 62, 592, 0, 116, 1, 0, 0, 0, 2, 119, 1, 0, 0, 0, 4, 136, 1, 0, 0, 0, 6, 152, 1, 0, 0, 0, 8, 154, 1, 0, 0, 0, 10, 185, 1, 0, 0, 0, 12, 212, 1, 0, 0, 0, 14, 219, 1, 0, 0, 0, 16, 225, 1, 0, 0, 0, 18, 246, 1, 0, 0, 0, 20, 256, 1, 0, 0, 0, 22, 271, 1, 0, 0, 0, 24, 273, 1, 0, 0, 0, 26, 276, 1, 0, 0, 0, 28, 289, 1, 0, 0, 0, 30, 291, 1, 0, 0, 0, 32, 308, 1, 0, 0, 0, 34, 310, 1, 0, 0, 0, 36, 312, 1, 0, 0, 0, 38, 316, 1, 0, 0, 0, 40, 318, 1, 0, 0, 0, 42, 327, 1, 0, 0, 0, 44, 331, 1, 0, 0, 0, 46, 347, 1, 0, 0, 0, 48, 350, 1, 0, 0, 0, 50, 358, 1, 0, 0, 0, 52, 364, 1, 0, 0, 0, 54, 372, 1, 0, 0, 0, 56, 380, 1, 0, 0, 0, 58, 388, 1, 0, 0, 0, 60, 390, 1, 0, 0, 0, 62, 434, 1, 0, 0, 0, 64, 438, 1, 0, 0, 0, 66, 440, 1, 0, 0, 0, 68, 443, 1, 0, 0, 0, 70, 452, 1, 0, 0, 0, 72, 460, 1, 0, 0, 0, 74, 463, 1, 0, 0, 0, 76, 466, 1, 0, 0, 0, 78, 475, 1, 0, 0, 0, 80, 479, 1, 0, 0, 0, 82, 485, 1, 0, 0, 0, 84, 489, 1, 0, 0, 0, 86, 492, 1, 0, 0, 0, 88, 500, 1, 0, 0, 0, 90, 504, 1, 0, 0, 0, 92, 508, 1, 0, 0, 0, 94, 511, 1, 0, 0, 0, 96, 516, 1, 0, 0, 0, 98, 520, 1, 0, 0, 0, 100, 522, 1, 0, 0, 0, 102, 524, 1, 0, 0, 0, 104, 527, 1, 0, 0, 0, 106, 531, 1, 0, 0, 0, 108, 534, 1, 0, 0, 0, 110, 537, 1, 0, 0, 0, 112, 557, 1, 0, 0, 0, 114, 561, 1, 0, 0, 0, 116, 117, 3, 2, 1, 0, 117, 118, 5, 0, 0, 1, 118, 1, 1, 0, 0, 0, 119, 120, 6, 1, -1, 0, 120, 121, 3, 4, 2, 0, 121, 127, 1, 0, 0, 0, 122, 123, 10, 1, 0, 0, 123, 124, 5, 29, 0, 0, 124, 126, 3, 6, 3, 0, 125, 122, 1, 0, 0, 0, 126, 129, 1, 0, 0, 0, 127, 125, 1, 0, 0, 0, 127, 128, 1, 0, 0, 0, 128, 3, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 130, 137, 3, 102, 51, 0, 131, 137, 3, 30, 15, 0, 132, 137, 3, 24, 12, 0, 133, 137, 3, 44, 22, 0, 134, 137, 3, 106, 53, 0, 135, 137, 3, 108, 54, 0, 136, 130, 1, 0, 0, 0, 136, 131, 1, 0, 0, 0, 136, 132, 1, 0, 0, 0, 136, 133, 1, 0, 0, 0, 136, 134, 1, 0, 0, 0, 136, 135, 1, 0, 0, 0, 137, 5, 1, 0, 0, 0, 138, 153, 3, 46, 23, 0, 139, 153, 3, 50, 25, 0, 140, 153, 3, 66, 33, 0, 141, 153, 3, 114, 57, 0, 142, 153, 3, 72, 36, 0, 143, 153, 3, 68, 34, 0, 144, 153, 3, 48, 24, 0, 145, 153, 3, 8, 4, 0, 146, 153, 3, 74, 37, 0, 147, 153, 3, 76, 38, 0, 148, 153, 3, 80, 40, 0, 149, 153, 3, 82, 41, 0, 150, 153, 3, 110, 55, 0, 151, 153, 3, 84, 42, 0, 152, 138, 1, 0, 0, 0, 152, 139, 1, 0, 0, 0, 152, 140, 1, 0, 0, 0, 152, 141, 1, 0, 0, 0, 152, 142, 1, 0, 0, 0, 152, 143, 1, 0, 0, 0, 152, 144, 1, 0, 0, 0, 152, 145, 1, 0, 0, 0, 152, 146, 1, 0, 0, 0, 152, 147, 1, 0, 0, 0, 152, 148, 1, 0, 0, 0, 152, 149, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 152, 151, 1, 0, 0, 0, 153, 7, 1, 0, 0, 0, 154, 155, 5, 20, 0, 0, 155, 156, 3, 10, 5, 0, 156, 9, 1, 0, 0, 0, 157, 158, 6, 5, -1, 0, 158, 159, 5, 48, 0, 0, 159, 186, 3, 10, 5, 7, 160, 186, 3, 14, 7, 0, 161, 186, 3, 12, 6, 0, 162, 164, 3, 14, 7, 0, 163, 165, 5, 48, 0, 0, 164, 163, 1, 0, 0, 0, 164, 165, 1, 0, 0, 0, 165, 166, 1, 0, 0, 0, 166, 167, 5, 45, 0, 0, 167, 168, 5, 44, 0, 0, 168, 173, 3, 14, 7, 0, 169, 170, 5, 38, 0, 0, 170, 172, 3, 14, 7, 0, 171, 169, 1, 0, 0, 0, 172, 175, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 173, 174, 1, 0, 0, 0, 174, 176, 1, 0, 0, 0, 175, 173, 1, 0, 0, 0, 176, 177, 5, 54, 0, 0, 177, 186, 1, 0, 0, 0, 178, 179, 3, 14, 7, 0, 179, 181, 5, 46, 0, 0, 180, 182, 5, 48, 0, 0, 181, 180, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 183, 1, 0, 0, 0, 183, 184, 5, 49, 0, 0, 184, 186, 1, 0, 0, 0, 185, 157, 1, 0, 0, 0, 185, 160, 1, 0, 0, 0, 185, 161, 1, 0, 0, 0, 185, 162, 1, 0, 0, 0, 185, 178, 1, 0, 0, 0, 186, 195, 1, 0, 0, 0, 187, 188, 10, 4, 0, 0, 188, 189, 5, 34, 0, 0, 189, 194, 3, 10, 5, 5, 190, 191, 10, 3, 0, 0, 191, 192, 5, 51, 0, 0, 192, 194, 3, 10, 5, 4, 193, 187, 1, 0, 0, 0, 193, 190, 1, 0, 0, 0, 194, 197, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 195, 196, 1, 0, 0, 0, 196, 11, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 200, 3, 14, 7, 0, 199, 201, 5, 48, 0, 0, 200, 199, 1, 0, 0, 0, 200, 201, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 5, 47, 0, 0, 203, 204, 3, 98, 49, 0, 204, 213, 1, 0, 0, 0, 205, 207, 3, 14, 7, 0, 206, 208, 5, 48, 0, 0, 207, 206, 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 209, 1, 0, 0, 0, 209, 210, 5, 53, 0, 0, 210, 211, 3, 98, 49, 0, 211, 213, 1, 0, 0, 0, 212, 198, 1, 0, 0, 0, 212, 205, 1, 0, 0, 0, 213, 13, 1, 0, 0, 0, 214, 220, 3, 16, 8, 0, 215, 216, 3, 16, 8, 0, 216, 217, 3, 100, 50, 0, 217, 218, 3, 16, 8, 0, 218, 220, 1, 0, 0, 0, 219, 214, 1, 0, 0, 0, 219, 215, 1, 0, 0, 0, 220, 15, 1, 0, 0, 0, 221, 222, 6, 8, -1, 0, 222, 226, 3, 18, 9, 0, 223, 224, 7, 0, 0, 0, 224, 226, 3, 16, 8, 3, 225, 221, 1, 0, 0, 0, 225, 223, 1, 0, 0, 0, 226, 235, 1, 0, 0, 0, 227, 228, 10, 2, 0, 0, 228, 229, 7, 1, 0, 0, 229, 234, 3, 16, 8, 3, 230, 231, 10, 1, 0, 0, 231, 232, 7, 0, 0, 0, 232, 234, 3, 16, 8, 2, 233, 227, 1, 0, 0, 0, 233, 230, 1, 0, 0, 0, 234, 237, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 235, 236, 1, 0, 0, 0, 236, 17, 1, 0, 0, 0, 237, 235, 1, 0, 0, 0, 238, 239, 6, 9, -1, 0, 239, 247, 3, 62, 31, 0, 240, 247, 3, 52, 26, 0, 241, 247, 3, 20, 10, 0, 242, 243, 5, 44, 0, 0, 243, 244, 3, 10, 5, 0, 244, 245, 5, 54, 0, 0, 245, 247, 1, 0, 0, 0, 246, 238, 1, 0, 0, 0, 246, 240, 1, 0, 0, 0, 246, 241, 1, 0, 0, 0, 246, 242, 1, 0, 0, 0, 247, 253, 1, 0, 0, 0, 248, 249, 10, 1, 0, 0, 249, 250, 5, 37, 0, 0, 250, 252, 3, 22, 11, 0, 251, 248, 1, 0, 0, 0, 252, 255, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 19, 1, 0, 0, 0, 255, 253, 1, 0, 0, 0, 256, 257, 3, 58, 29, 0, 257, 267, 5, 44, 0, 0, 258, 268, 5, 65, 0, 0, 259, 264, 3, 10, 5, 0, 260, 261, 5, 38, 0, 0, 261, 263, 3, 10, 5, 0, 262, 260, 1, 0, 0, 0, 263, 266, 1, 0, 0, 0, 264, 262, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 268, 1, 0, 0, 0, 266, 264, 1, 0, 0, 0, 267, 258, 1, 0, 0, 0, 267, 259, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 270, 5, 54, 0, 0, 270, 21, 1, 0, 0, 0, 271, 272, 3, 58, 29, 0, 272, 23, 1, 0, 0, 0, 273, 274, 5, 16, 0, 0, 274, 275, 3, 26, 13, 0, 275, 25, 1, 0, 0, 0, 276, 281, 3, 28, 14, 0, 277, 278, 5, 38, 0, 0, 278, 280, 3, 28, 14, 0, 279, 277, 1, 0, 0, 0, 280, 283, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0, 281, 282, 1, 0, 0, 0, 282, 27, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 284, 290, 3, 10, 5, 0, 285, 286, 3, 52, 26, 0, 286, 287, 5, 36, 0, 0, 287, 288, 3, 10, 5, 0, 288, 290, 1, 0, 0, 0, 289, 284, 1, 0, 0, 0, 289, 285, 1, 0, 0, 0, 290, 29, 1, 0, 0, 0, 291, 292, 5, 6, 0, 0, 292, 297, 3, 32, 16, 0, 293, 294, 5, 38, 0, 0, 294, 296, 3, 32, 16, 0, 295, 293, 1, 0, 0, 0, 296, 299, 1, 0, 0, 0, 297, 295, 1, 0, 0, 0, 297, 298, 1, 0, 0, 0, 298, 301, 1, 0, 0, 0, 299, 297, 1, 0, 0, 0, 300, 302, 3, 38, 19, 0, 301, 300, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 31, 1, 0, 0, 0, 303, 304, 3, 34, 17, 0, 304, 305, 5, 114, 0, 0, 305, 306, 3, 36, 18, 0, 306, 309, 1, 0, 0, 0, 307, 309, 3, 36, 18, 0, 308, 303, 1, 0, 0, 0, 308, 307, 1, 0, 0, 0, 309, 33, 1, 0, 0, 0, 310, 311, 5, 25, 0, 0, 311, 35, 1, 0, 0, 0, 312, 313, 7, 2, 0, 0, 313, 37, 1, 0, 0, 0, 314, 317, 3, 40, 20, 0, 315, 317, 3, 42, 21, 0, 316, 314, 1, 0, 0, 0, 316, 315, 1, 0, 0, 0, 317, 39, 1, 0, 0, 0, 318, 319, 5, 76, 0, 0, 319, 324, 5, 25, 0, 0, 320, 321, 5, 38, 0, 0, 321, 323, 5, 25, 0, 0, 322, 320, 1, 0, 0, 0, 323, 326, 1, 0, 0, 0, 324, 322, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 41, 1, 0, 0, 0, 326, 324, 1, 0, 0, 0, 327, 328, 5, 69, 0, 0, 328, 329, 3, 40, 20, 0, 329, 330, 5, 70, 0, 0, 330, 43, 1, 0, 0, 0, 331, 332, 5, 13, 0, 0, 332, 337, 3, 32, 16, 0, 333, 334, 5, 38, 0, 0, 334, 336, 3, 32, 16, 0, 335, 333, 1, 0, 0, 0, 336, 339, 1, 0, 0, 0, 337, 335, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 341, 1, 0, 0, 0, 339, 337, 1, 0, 0, 0, 340, 342, 3, 26, 13, 0, 341, 340, 1, 0, 0, 0, 341, 342, 1, 0, 0, 0, 342, 345, 1, 0, 0, 0, 343, 344, 5, 33, 0, 0, 344, 346, 3, 26, 13, 0, 345, 343, 1, 0, 0, 0, 345, 346, 1, 0, 0, 0, 346, 45, 1, 0, 0, 0, 347, 348, 5, 4, 0, 0, 348, 349, 3, 26, 13, 0, 349, 47, 1, 0, 0, 0, 350, 352, 5, 19, 0, 0, 351, 353, 3, 26, 13, 0, 352, 351, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 356, 1, 0, 0, 0, 354, 355, 5, 33, 0, 0, 355, 357, 3, 26, 13, 0, 356, 354, 1, 0, 0, 0, 356, 357, 1, 0, 0, 0, 357, 49, 1, 0, 0, 0, 358, 359, 5, 8, 0, 0, 359, 362, 3, 26, 13, 0, 360, 361, 5, 33, 0, 0, 361, 363, 3, 26, 13, 0, 362, 360, 1, 0, 0, 0, 362, 363, 1, 0, 0, 0, 363, 51, 1, 0, 0, 0, 364, 369, 3, 58, 29, 0, 365, 366, 5, 40, 0, 0, 366, 368, 3, 58, 29, 0, 367, 365, 1, 0, 0, 0, 368, 371, 1, 0, 0, 0, 369, 367, 1, 0, 0, 0, 369, 370, 1, 0, 0, 0, 370, 53, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 372, 377, 3, 60, 30, 0, 373, 374, 5, 40, 0, 0, 374, 376, 3, 60, 30, 0, 375, 373, 1, 0, 0, 0, 376, 379, 1, 0, 0, 0, 377, 375, 1, 0, 0, 0, 377, 378, 1, 0, 0, 0, 378, 55, 1, 0, 0, 0, 379, 377, 1, 0, 0, 0, 380, 385, 3, 54, 27, 0, 381, 382, 5, 38, 0, 0, 382, 384, 3, 54, 27, 0, 383, 381, 1, 0, 0, 0, 384, 387, 1, 0, 0, 0, 385, 383, 1, 0, 0, 0, 385, 386, 1, 0, 0, 0, 386, 57, 1, 0, 0, 0, 387, 385, 1, 0, 0, 0, 388, 389, 7, 3, 0, 0, 389, 59, 1, 0, 0, 0, 390, 391, 5, 80, 0, 0, 391, 61, 1, 0, 0, 0, 392, 435, 5, 49, 0, 0, 393, 394, 3, 96, 48, 0, 394, 395, 5, 71, 0, 0, 395, 435, 1, 0, 0, 0, 396, 435, 3, 94, 47, 0, 397, 435, 3, 96, 48, 0, 398, 435, 3, 90, 45, 0, 399, 435, 3, 64, 32, 0, 400, 435, 3, 98, 49, 0, 401, 402, 5, 69, 0, 0, 402, 407, 3, 92, 46, 0, 403, 404, 5, 38, 0, 0, 404, 406, 3, 92, 46, 0, 405, 403, 1, 0, 0, 0, 406, 409, 1, 0, 0, 0, 407, 405, 1, 0, 0, 0, 407, 408, 1, 0, 0, 0, 408, 410, 1, 0, 0, 0, 409, 407, 1, 0, 0, 0, 410, 411, 5, 70, 0, 0, 411, 435, 1, 0, 0, 0, 412, 413, 5, 69, 0, 0, 413, 418, 3, 90, 45, 0, 414, 415, 5, 38, 0, 0, 415, 417, 3, 90, 45, 0, 416, 414, 1, 0, 0, 0, 417, 420, 1, 0, 0, 0, 418, 416, 1, 0, 0, 0, 418, 419, 1, 0, 0, 0, 419, 421, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 421, 422, 5, 70, 0, 0, 422, 435, 1, 0, 0, 0, 423, 424, 5, 69, 0, 0, 424, 429, 3, 98, 49, 0, 425, 426, 5, 38, 0, 0, 426, 428, 3, 98, 49, 0, 427, 425, 1, 0, 0, 0, 428, 431, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 429, 430, 1, 0, 0, 0, 430, 432, 1, 0, 0, 0, 431, 429, 1, 0, 0, 0, 432, 433, 5, 70, 0, 0, 433, 435, 1, 0, 0, 0, 434, 392, 1, 0, 0, 0, 434, 393, 1, 0, 0, 0, 434, 396, 1, 0, 0, 0, 434, 397, 1, 0, 0, 0, 434, 398, 1, 0, 0, 0, 434, 399, 1, 0, 0, 0, 434, 400, 1, 0, 0, 0, 434, 401, 1, 0, 0, 0, 434, 412, 1, 0, 0, 0, 434, 423, 1, 0, 0, 0, 435, 63, 1, 0, 0, 0, 436, 439, 5, 52, 0, 0, 437, 439, 5, 68, 0, 0, 438, 436, 1, 0, 0, 0, 438, 437, 1, 0, 0, 0, 439, 65, 1, 0, 0, 0, 440, 441, 5, 10, 0, 0, 441, 442, 5, 31, 0, 0, 442, 67, 1, 0, 0, 0, 443, 444, 5, 18, 0, 0, 444, 449, 3, 70, 35, 0, 445, 446, 5, 38, 0, 0, 446, 448, 3, 70, 35, 0, 447, 445, 1, 0, 0, 0, 448, 451, 1, 0, 0, 0, 449, 447, 1, 0, 0, 0, 449, 450, 1, 0, 0, 0, 450, 69, 1, 0, 0, 0, 451, 449, 1, 0, 0, 0, 452, 454, 3, 10, 5, 0, 453, 455, 7, 4, 0, 0, 454, 453, 1, 0, 0, 0, 454, 455, 1, 0, 0, 0, 455, 458, 1, 0, 0, 0, 456, 457, 5, 50, 0, 0, 457, 459, 7, 5, 0, 0, 458, 456, 1, 0, 0, 0, 458, 459, 1, 0, 0, 0, 459, 71, 1, 0, 0, 0, 460, 461, 5, 9, 0, 0, 461, 462, 3, 56, 28, 0, 462, 73, 1, 0, 0, 0, 463, 464, 5, 2, 0, 0, 464, 465, 3, 56, 28, 0, 465, 75, 1, 0, 0, 0, 466, 467, 5, 15, 0, 0, 467, 472, 3, 78, 39, 0, 468, 469, 5, 38, 0, 0, 469, 471, 3, 78, 39, 0, 470, 468, 1, 0, 0, 0, 471, 474, 1, 0, 0, 0, 472, 470, 1, 0, 0, 0, 472, 473, 1, 0, 0, 0, 473, 77, 1, 0, 0, 0, 474, 472, 1, 0, 0, 0, 475, 476, 3, 54, 27, 0, 476, 477, 5, 84, 0, 0, 477, 478, 3, 54, 27, 0, 478, 79, 1, 0, 0, 0, 479, 480, 5, 1, 0, 0, 480, 481, 3, 18, 9, 0, 481, 483, 3, 98, 49, 0, 482, 484, 3, 86, 43, 0, 483, 482, 1, 0, 0, 0, 483, 484, 1, 0, 0, 0, 484, 81, 1, 0, 0, 0, 485, 486, 5, 7, 0, 0, 486, 487, 3, 18, 9, 0, 487, 488, 3, 98, 49, 0, 488, 83, 1, 0, 0, 0, 489, 490, 5, 14, 0, 0, 490, 491, 3, 52, 26, 0, 491, 85, 1, 0, 0, 0, 492, 497, 3, 88, 44, 0, 493, 494, 5, 38, 0, 0, 494, 496, 3, 88, 44, 0, 495, 493, 1, 0, 0, 0, 496, 499, 1, 0, 0, 0, 497, 495, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 87, 1, 0, 0, 0, 499, 497, 1, 0, 0, 0, 500, 501, 3, 58, 29, 0, 501, 502, 5, 36, 0, 0, 502, 503, 3, 62, 31, 0, 503, 89, 1, 0, 0, 0, 504, 505, 7, 6, 0, 0, 505, 91, 1, 0, 0, 0, 506, 509, 3, 94, 47, 0, 507, 509, 3, 96, 48, 0, 508, 506, 1, 0, 0, 0, 508, 507, 1, 0, 0, 0, 509, 93, 1, 0, 0, 0, 510, 512, 7, 0, 0, 0, 511, 510, 1, 0, 0, 0, 511, 512, 1, 0, 0, 0, 512, 513, 1, 0, 0, 0, 513, 514, 5, 32, 0, 0, 514, 95, 1, 0, 0, 0, 515, 517, 7, 0, 0, 0, 516, 515, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 519, 5, 31, 0, 0, 519, 97, 1, 0, 0, 0, 520, 521, 5, 30, 0, 0, 521, 99, 1, 0, 0, 0, 522, 523, 7, 7, 0, 0, 523, 101, 1, 0, 0, 0, 524, 525, 5, 5, 0, 0, 525, 526, 3, 104, 52, 0, 526, 103, 1, 0, 0, 0, 527, 528, 5, 69, 0, 0, 528, 529, 3, 2, 1, 0, 529, 530, 5, 70, 0, 0, 530, 105, 1, 0, 0, 0, 531, 532, 5, 17, 0, 0, 532, 533, 5, 106, 0, 0, 533, 107, 1, 0, 0, 0, 534, 535, 5, 12, 0, 0, 535, 536, 5, 110, 0, 0, 536, 109, 1, 0, 0, 0, 537, 538, 5, 3, 0, 0, 538, 541, 5, 90, 0, 0, 539, 540, 5, 88, 0, 0, 540, 542, 3, 54, 27, 0, 541, 539, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 552, 1, 0, 0, 0, 543, 544, 5, 89, 0, 0, 544, 549, 3, 112, 56, 0, 545, 546, 5, 38, 0, 0, 546, 548, 3, 112, 56, 0, 547, 545, 1, 0, 0, 0, 548, 551, 1, 0, 0, 0, 549, 547, 1, 0, 0, 0, 549, 550, 1, 0, 0, 0, 550, 553, 1, 0, 0, 0, 551, 549, 1, 0, 0, 0, 552, 543, 1, 0, 0, 0, 552, 553, 1, 0, 0, 0, 553, 111, 1, 0, 0, 0, 554, 555, 3, 54, 27, 0, 555, 556, 5, 36, 0, 0, 556, 558, 1, 0, 0, 0, 557, 554, 1, 0, 0, 0, 557, 558, 1, 0, 0, 0, 558, 559, 1, 0, 0, 0, 559, 560, 3, 54, 27, 0, 560, 113, 1, 0, 0, 0, 561, 562, 5, 11, 0, 0, 562, 563, 3, 32, 16, 0, 563, 564, 5, 88, 0, 0, 564, 565, 3, 56, 28, 0, 565, 115, 1, 0, 0, 0, 54, 127, 136, 152, 164, 173, 181, 185, 193, 195, 200, 207, 212, 219, 225, 233, 235, 246, 253, 264, 267, 281, 289, 297, 301, 308, 316, 324, 337, 341, 345, 352, 356, 362, 369, 377, 385, 407, 418, 429, 434, 438, 449, 454, 458, 472, 483, 497, 508, 511, 516, 541, 549, 552, 557] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens index 04798fc3dca8a..63eb3a86419a3 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens @@ -22,7 +22,7 @@ UNKNOWN_CMD=21 LINE_COMMENT=22 MULTILINE_COMMENT=23 WS=24 -INDEX_UNQUOTED_IDENTIFIER=25 +UNQUOTED_SOURCE=25 EXPLAIN_WS=26 EXPLAIN_LINE_COMMENT=27 EXPLAIN_MULTILINE_COMMENT=28 diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.ts b/packages/kbn-esql-ast/src/antlr/esql_parser.ts index 5181349c62c88..1c8ce9f0d86e5 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.ts @@ -42,7 +42,7 @@ export default class esql_parser extends Parser { public static readonly LINE_COMMENT = 22; public static readonly MULTILINE_COMMENT = 23; public static readonly WS = 24; - public static readonly INDEX_UNQUOTED_IDENTIFIER = 25; + public static readonly UNQUOTED_SOURCE = 25; public static readonly EXPLAIN_WS = 26; public static readonly EXPLAIN_LINE_COMMENT = 27; public static readonly EXPLAIN_MULTILINE_COMMENT = 28; @@ -159,213 +159,216 @@ export default class esql_parser extends Parser { public static readonly RULE_fields = 13; public static readonly RULE_field = 14; public static readonly RULE_fromCommand = 15; - public static readonly RULE_indexIdentifier = 16; - public static readonly RULE_metadata = 17; - public static readonly RULE_metadataOption = 18; - public static readonly RULE_deprecated_metadata = 19; - public static readonly RULE_metricsCommand = 20; - public static readonly RULE_evalCommand = 21; - public static readonly RULE_statsCommand = 22; - public static readonly RULE_inlinestatsCommand = 23; - public static readonly RULE_qualifiedName = 24; - public static readonly RULE_qualifiedNamePattern = 25; - public static readonly RULE_qualifiedNamePatterns = 26; - public static readonly RULE_identifier = 27; - public static readonly RULE_identifierPattern = 28; - public static readonly RULE_constant = 29; - public static readonly RULE_params = 30; - public static readonly RULE_limitCommand = 31; - public static readonly RULE_sortCommand = 32; - public static readonly RULE_orderExpression = 33; - public static readonly RULE_keepCommand = 34; - public static readonly RULE_dropCommand = 35; - public static readonly RULE_renameCommand = 36; - public static readonly RULE_renameClause = 37; - public static readonly RULE_dissectCommand = 38; - public static readonly RULE_grokCommand = 39; - public static readonly RULE_mvExpandCommand = 40; - public static readonly RULE_commandOptions = 41; - public static readonly RULE_commandOption = 42; - public static readonly RULE_booleanValue = 43; - public static readonly RULE_numericValue = 44; - public static readonly RULE_decimalValue = 45; - public static readonly RULE_integerValue = 46; - public static readonly RULE_string = 47; - public static readonly RULE_comparisonOperator = 48; - public static readonly RULE_explainCommand = 49; - public static readonly RULE_subqueryExpression = 50; - public static readonly RULE_showCommand = 51; - public static readonly RULE_metaCommand = 52; - public static readonly RULE_enrichCommand = 53; - public static readonly RULE_enrichWithClause = 54; - public static readonly RULE_lookupCommand = 55; - public static readonly literalNames: (string | null)[] = [ null, "'dissect'", - "'drop'", "'enrich'", - "'eval'", "'explain'", - "'from'", "'grok'", - "'inlinestats'", - "'keep'", "'limit'", - "'lookup'", - "'meta'", "'metrics'", - "'mv_expand'", - "'rename'", - "'row'", "'show'", - "'sort'", "'stats'", - "'where'", null, - null, null, - null, null, - null, null, - null, "'|'", - null, null, - null, "'by'", - "'and'", "'asc'", - "'='", "'::'", - "','", "'desc'", - "'.'", "'false'", - "'first'", "'last'", - "'('", "'in'", - "'is'", "'like'", - "'not'", "'null'", - "'nulls'", "'or'", - "'?'", "'rlike'", - "')'", "'true'", - "'=='", "'=~'", - "'!='", "'<'", - "'<='", "'>'", - "'>='", "'+'", - "'-'", "'*'", - "'/'", "'%'", - null, null, - "']'", null, - null, null, - null, null, - "'metadata'", - null, null, - null, null, - null, null, - null, "'as'", - null, null, - null, "'on'", - "'with'", null, - null, null, - null, null, - null, null, - null, null, - null, null, - null, null, - null, null, - null, "'info'", - null, null, - null, "'functions'", - null, null, + public static readonly RULE_indexPattern = 16; + public static readonly RULE_clusterString = 17; + public static readonly RULE_indexString = 18; + public static readonly RULE_metadata = 19; + public static readonly RULE_metadataOption = 20; + public static readonly RULE_deprecated_metadata = 21; + public static readonly RULE_metricsCommand = 22; + public static readonly RULE_evalCommand = 23; + public static readonly RULE_statsCommand = 24; + public static readonly RULE_inlinestatsCommand = 25; + public static readonly RULE_qualifiedName = 26; + public static readonly RULE_qualifiedNamePattern = 27; + public static readonly RULE_qualifiedNamePatterns = 28; + public static readonly RULE_identifier = 29; + public static readonly RULE_identifierPattern = 30; + public static readonly RULE_constant = 31; + public static readonly RULE_params = 32; + public static readonly RULE_limitCommand = 33; + public static readonly RULE_sortCommand = 34; + public static readonly RULE_orderExpression = 35; + public static readonly RULE_keepCommand = 36; + public static readonly RULE_dropCommand = 37; + public static readonly RULE_renameCommand = 38; + public static readonly RULE_renameClause = 39; + public static readonly RULE_dissectCommand = 40; + public static readonly RULE_grokCommand = 41; + public static readonly RULE_mvExpandCommand = 42; + public static readonly RULE_commandOptions = 43; + public static readonly RULE_commandOption = 44; + public static readonly RULE_booleanValue = 45; + public static readonly RULE_numericValue = 46; + public static readonly RULE_decimalValue = 47; + public static readonly RULE_integerValue = 48; + public static readonly RULE_string = 49; + public static readonly RULE_comparisonOperator = 50; + public static readonly RULE_explainCommand = 51; + public static readonly RULE_subqueryExpression = 52; + public static readonly RULE_showCommand = 53; + public static readonly RULE_metaCommand = 54; + public static readonly RULE_enrichCommand = 55; + public static readonly RULE_enrichWithClause = 56; + public static readonly RULE_lookupCommand = 57; + public static readonly literalNames: (string | null)[] = [ null, "'dissect'", + "'drop'", "'enrich'", + "'eval'", "'explain'", + "'from'", "'grok'", + "'inlinestats'", + "'keep'", "'limit'", + "'lookup'", + "'meta'", "'metrics'", + "'mv_expand'", + "'rename'", + "'row'", "'show'", + "'sort'", "'stats'", + "'where'", null, + null, null, + null, null, + null, null, + null, "'|'", + null, null, + null, "'by'", + "'and'", "'asc'", + "'='", "'::'", + "','", "'desc'", + "'.'", "'false'", + "'first'", "'last'", + "'('", "'in'", + "'is'", "'like'", + "'not'", "'null'", + "'nulls'", "'or'", + "'?'", "'rlike'", + "')'", "'true'", + "'=='", "'=~'", + "'!='", "'<'", + "'<='", "'>'", + "'>='", "'+'", + "'-'", "'*'", + "'/'", "'%'", + null, null, + "']'", null, + null, null, + null, null, + "'metadata'", + null, null, + null, null, + null, null, + null, "'as'", + null, null, + null, "'on'", + "'with'", null, + null, null, + null, null, + null, null, + null, null, + null, null, + null, null, + null, null, + null, "'info'", + null, null, + null, "'functions'", + null, null, null, "':'" ]; - public static readonly symbolicNames: (string | null)[] = [ null, "DISSECT", - "DROP", "ENRICH", - "EVAL", "EXPLAIN", - "FROM", "GROK", - "INLINESTATS", - "KEEP", "LIMIT", - "LOOKUP", "META", - "METRICS", - "MV_EXPAND", - "RENAME", "ROW", - "SHOW", "SORT", - "STATS", "WHERE", - "UNKNOWN_CMD", - "LINE_COMMENT", - "MULTILINE_COMMENT", - "WS", "INDEX_UNQUOTED_IDENTIFIER", - "EXPLAIN_WS", - "EXPLAIN_LINE_COMMENT", - "EXPLAIN_MULTILINE_COMMENT", - "PIPE", "QUOTED_STRING", - "INTEGER_LITERAL", - "DECIMAL_LITERAL", - "BY", "AND", - "ASC", "ASSIGN", - "CAST_OP", - "COMMA", "DESC", - "DOT", "FALSE", - "FIRST", "LAST", - "LP", "IN", - "IS", "LIKE", - "NOT", "NULL", - "NULLS", "OR", - "PARAM", "RLIKE", - "RP", "TRUE", - "EQ", "CIEQ", - "NEQ", "LT", - "LTE", "GT", - "GTE", "PLUS", - "MINUS", "ASTERISK", - "SLASH", "PERCENT", - "NAMED_OR_POSITIONAL_PARAM", - "OPENING_BRACKET", - "CLOSING_BRACKET", - "UNQUOTED_IDENTIFIER", - "QUOTED_IDENTIFIER", - "EXPR_LINE_COMMENT", - "EXPR_MULTILINE_COMMENT", - "EXPR_WS", - "METADATA", - "FROM_LINE_COMMENT", - "FROM_MULTILINE_COMMENT", - "FROM_WS", - "ID_PATTERN", - "PROJECT_LINE_COMMENT", - "PROJECT_MULTILINE_COMMENT", - "PROJECT_WS", - "AS", "RENAME_LINE_COMMENT", - "RENAME_MULTILINE_COMMENT", - "RENAME_WS", - "ON", "WITH", - "ENRICH_POLICY_NAME", - "ENRICH_LINE_COMMENT", - "ENRICH_MULTILINE_COMMENT", - "ENRICH_WS", - "ENRICH_FIELD_LINE_COMMENT", - "ENRICH_FIELD_MULTILINE_COMMENT", - "ENRICH_FIELD_WS", - "LOOKUP_LINE_COMMENT", - "LOOKUP_MULTILINE_COMMENT", - "LOOKUP_WS", - "LOOKUP_FIELD_LINE_COMMENT", - "LOOKUP_FIELD_MULTILINE_COMMENT", - "LOOKUP_FIELD_WS", - "MVEXPAND_LINE_COMMENT", - "MVEXPAND_MULTILINE_COMMENT", - "MVEXPAND_WS", - "INFO", "SHOW_LINE_COMMENT", - "SHOW_MULTILINE_COMMENT", - "SHOW_WS", - "FUNCTIONS", - "META_LINE_COMMENT", - "META_MULTILINE_COMMENT", - "META_WS", - "COLON", "SETTING", - "SETTING_LINE_COMMENT", - "SETTTING_MULTILINE_COMMENT", - "SETTING_WS", - "METRICS_LINE_COMMENT", - "METRICS_MULTILINE_COMMENT", - "METRICS_WS", - "CLOSING_METRICS_LINE_COMMENT", - "CLOSING_METRICS_MULTILINE_COMMENT", + public static readonly symbolicNames: (string | null)[] = [ null, "DISSECT", + "DROP", "ENRICH", + "EVAL", "EXPLAIN", + "FROM", "GROK", + "INLINESTATS", + "KEEP", "LIMIT", + "LOOKUP", "META", + "METRICS", + "MV_EXPAND", + "RENAME", "ROW", + "SHOW", "SORT", + "STATS", "WHERE", + "UNKNOWN_CMD", + "LINE_COMMENT", + "MULTILINE_COMMENT", + "WS", "UNQUOTED_SOURCE", + "EXPLAIN_WS", + "EXPLAIN_LINE_COMMENT", + "EXPLAIN_MULTILINE_COMMENT", + "PIPE", "QUOTED_STRING", + "INTEGER_LITERAL", + "DECIMAL_LITERAL", + "BY", "AND", + "ASC", "ASSIGN", + "CAST_OP", + "COMMA", "DESC", + "DOT", "FALSE", + "FIRST", "LAST", + "LP", "IN", + "IS", "LIKE", + "NOT", "NULL", + "NULLS", "OR", + "PARAM", "RLIKE", + "RP", "TRUE", + "EQ", "CIEQ", + "NEQ", "LT", + "LTE", "GT", + "GTE", "PLUS", + "MINUS", "ASTERISK", + "SLASH", "PERCENT", + "NAMED_OR_POSITIONAL_PARAM", + "OPENING_BRACKET", + "CLOSING_BRACKET", + "UNQUOTED_IDENTIFIER", + "QUOTED_IDENTIFIER", + "EXPR_LINE_COMMENT", + "EXPR_MULTILINE_COMMENT", + "EXPR_WS", + "METADATA", + "FROM_LINE_COMMENT", + "FROM_MULTILINE_COMMENT", + "FROM_WS", + "ID_PATTERN", + "PROJECT_LINE_COMMENT", + "PROJECT_MULTILINE_COMMENT", + "PROJECT_WS", + "AS", "RENAME_LINE_COMMENT", + "RENAME_MULTILINE_COMMENT", + "RENAME_WS", + "ON", "WITH", + "ENRICH_POLICY_NAME", + "ENRICH_LINE_COMMENT", + "ENRICH_MULTILINE_COMMENT", + "ENRICH_WS", + "ENRICH_FIELD_LINE_COMMENT", + "ENRICH_FIELD_MULTILINE_COMMENT", + "ENRICH_FIELD_WS", + "LOOKUP_LINE_COMMENT", + "LOOKUP_MULTILINE_COMMENT", + "LOOKUP_WS", + "LOOKUP_FIELD_LINE_COMMENT", + "LOOKUP_FIELD_MULTILINE_COMMENT", + "LOOKUP_FIELD_WS", + "MVEXPAND_LINE_COMMENT", + "MVEXPAND_MULTILINE_COMMENT", + "MVEXPAND_WS", + "INFO", "SHOW_LINE_COMMENT", + "SHOW_MULTILINE_COMMENT", + "SHOW_WS", + "FUNCTIONS", + "META_LINE_COMMENT", + "META_MULTILINE_COMMENT", + "META_WS", + "COLON", "SETTING", + "SETTING_LINE_COMMENT", + "SETTTING_MULTILINE_COMMENT", + "SETTING_WS", + "METRICS_LINE_COMMENT", + "METRICS_MULTILINE_COMMENT", + "METRICS_WS", + "CLOSING_METRICS_LINE_COMMENT", + "CLOSING_METRICS_MULTILINE_COMMENT", "CLOSING_METRICS_WS" ]; // tslint:disable:no-trailing-whitespace public static readonly ruleNames: string[] = [ - "singleStatement", "query", "sourceCommand", "processingCommand", "whereCommand", - "booleanExpression", "regexBooleanExpression", "valueExpression", "operatorExpression", - "primaryExpression", "functionExpression", "dataType", "rowCommand", "fields", - "field", "fromCommand", "indexIdentifier", "metadata", "metadataOption", - "deprecated_metadata", "metricsCommand", "evalCommand", "statsCommand", - "inlinestatsCommand", "qualifiedName", "qualifiedNamePattern", "qualifiedNamePatterns", - "identifier", "identifierPattern", "constant", "params", "limitCommand", - "sortCommand", "orderExpression", "keepCommand", "dropCommand", "renameCommand", - "renameClause", "dissectCommand", "grokCommand", "mvExpandCommand", "commandOptions", - "commandOption", "booleanValue", "numericValue", "decimalValue", "integerValue", - "string", "comparisonOperator", "explainCommand", "subqueryExpression", - "showCommand", "metaCommand", "enrichCommand", "enrichWithClause", "lookupCommand", + "singleStatement", "query", "sourceCommand", "processingCommand", "whereCommand", + "booleanExpression", "regexBooleanExpression", "valueExpression", "operatorExpression", + "primaryExpression", "functionExpression", "dataType", "rowCommand", "fields", + "field", "fromCommand", "indexPattern", "clusterString", "indexString", + "metadata", "metadataOption", "deprecated_metadata", "metricsCommand", + "evalCommand", "statsCommand", "inlinestatsCommand", "qualifiedName", + "qualifiedNamePattern", "qualifiedNamePatterns", "identifier", "identifierPattern", + "constant", "params", "limitCommand", "sortCommand", "orderExpression", + "keepCommand", "dropCommand", "renameCommand", "renameClause", "dissectCommand", + "grokCommand", "mvExpandCommand", "commandOptions", "commandOption", "booleanValue", + "numericValue", "decimalValue", "integerValue", "string", "comparisonOperator", + "explainCommand", "subqueryExpression", "showCommand", "metaCommand", + "enrichCommand", "enrichWithClause", "lookupCommand", ]; public get grammarFileName(): string { return "esql_parser.g4"; } public get literalNames(): (string | null)[] { return esql_parser.literalNames; } @@ -388,9 +391,9 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 112; + this.state = 116; this.query(0); - this.state = 113; + this.state = 117; this.match(esql_parser.EOF); } } @@ -432,11 +435,11 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 116; + this.state = 120; this.sourceCommand(); } this._ctx.stop = this._input.LT(-1); - this.state = 123; + this.state = 127; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 0, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -449,18 +452,18 @@ export default class esql_parser extends Parser { { localctx = new CompositeQueryContext(this, new QueryContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_query); - this.state = 118; + this.state = 122; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 119; + this.state = 123; this.match(esql_parser.PIPE); - this.state = 120; + this.state = 124; this.processingCommand(); } } } - this.state = 125; + this.state = 129; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 0, this._ctx); } @@ -485,48 +488,48 @@ export default class esql_parser extends Parser { let localctx: SourceCommandContext = new SourceCommandContext(this, this._ctx, this.state); this.enterRule(localctx, 4, esql_parser.RULE_sourceCommand); try { - this.state = 132; + this.state = 136; this._errHandler.sync(this); switch (this._input.LA(1)) { case 5: this.enterOuterAlt(localctx, 1); { - this.state = 126; + this.state = 130; this.explainCommand(); } break; case 6: this.enterOuterAlt(localctx, 2); { - this.state = 127; + this.state = 131; this.fromCommand(); } break; case 16: this.enterOuterAlt(localctx, 3); { - this.state = 128; + this.state = 132; this.rowCommand(); } break; case 13: this.enterOuterAlt(localctx, 4); { - this.state = 129; + this.state = 133; this.metricsCommand(); } break; case 17: this.enterOuterAlt(localctx, 5); { - this.state = 130; + this.state = 134; this.showCommand(); } break; case 12: this.enterOuterAlt(localctx, 6); { - this.state = 131; + this.state = 135; this.metaCommand(); } break; @@ -553,104 +556,104 @@ export default class esql_parser extends Parser { let localctx: ProcessingCommandContext = new ProcessingCommandContext(this, this._ctx, this.state); this.enterRule(localctx, 6, esql_parser.RULE_processingCommand); try { - this.state = 148; + this.state = 152; this._errHandler.sync(this); switch (this._input.LA(1)) { case 4: this.enterOuterAlt(localctx, 1); { - this.state = 134; + this.state = 138; this.evalCommand(); } break; case 8: this.enterOuterAlt(localctx, 2); { - this.state = 135; + this.state = 139; this.inlinestatsCommand(); } break; case 10: this.enterOuterAlt(localctx, 3); { - this.state = 136; + this.state = 140; this.limitCommand(); } break; case 11: this.enterOuterAlt(localctx, 4); { - this.state = 137; + this.state = 141; this.lookupCommand(); } break; case 9: this.enterOuterAlt(localctx, 5); { - this.state = 138; + this.state = 142; this.keepCommand(); } break; case 18: this.enterOuterAlt(localctx, 6); { - this.state = 139; + this.state = 143; this.sortCommand(); } break; case 19: this.enterOuterAlt(localctx, 7); { - this.state = 140; + this.state = 144; this.statsCommand(); } break; case 20: this.enterOuterAlt(localctx, 8); { - this.state = 141; + this.state = 145; this.whereCommand(); } break; case 2: this.enterOuterAlt(localctx, 9); { - this.state = 142; + this.state = 146; this.dropCommand(); } break; case 15: this.enterOuterAlt(localctx, 10); { - this.state = 143; + this.state = 147; this.renameCommand(); } break; case 1: this.enterOuterAlt(localctx, 11); { - this.state = 144; + this.state = 148; this.dissectCommand(); } break; case 7: this.enterOuterAlt(localctx, 12); { - this.state = 145; + this.state = 149; this.grokCommand(); } break; case 3: this.enterOuterAlt(localctx, 13); { - this.state = 146; + this.state = 150; this.enrichCommand(); } break; case 14: this.enterOuterAlt(localctx, 14); { - this.state = 147; + this.state = 151; this.mvExpandCommand(); } break; @@ -679,9 +682,9 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 150; + this.state = 154; this.match(esql_parser.WHERE); - this.state = 151; + this.state = 155; this.booleanExpression(0); } } @@ -719,7 +722,7 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 181; + this.state = 185; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 6, this._ctx) ) { case 1: @@ -728,9 +731,9 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 154; + this.state = 158; this.match(esql_parser.NOT); - this.state = 155; + this.state = 159; this.booleanExpression(7); } break; @@ -739,7 +742,7 @@ export default class esql_parser extends Parser { localctx = new BooleanDefaultContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 156; + this.state = 160; this.valueExpression(); } break; @@ -748,7 +751,7 @@ export default class esql_parser extends Parser { localctx = new RegexExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 157; + this.state = 161; this.regexBooleanExpression(); } break; @@ -757,41 +760,41 @@ export default class esql_parser extends Parser { localctx = new LogicalInContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 158; + this.state = 162; this.valueExpression(); - this.state = 160; + this.state = 164; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===48) { { - this.state = 159; + this.state = 163; this.match(esql_parser.NOT); } } - this.state = 162; + this.state = 166; this.match(esql_parser.IN); - this.state = 163; + this.state = 167; this.match(esql_parser.LP); - this.state = 164; + this.state = 168; this.valueExpression(); - this.state = 169; + this.state = 173; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===38) { { { - this.state = 165; + this.state = 169; this.match(esql_parser.COMMA); - this.state = 166; + this.state = 170; this.valueExpression(); } } - this.state = 171; + this.state = 175; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 172; + this.state = 176; this.match(esql_parser.RP); } break; @@ -800,27 +803,27 @@ export default class esql_parser extends Parser { localctx = new IsNullContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 174; + this.state = 178; this.valueExpression(); - this.state = 175; + this.state = 179; this.match(esql_parser.IS); - this.state = 177; + this.state = 181; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===48) { { - this.state = 176; + this.state = 180; this.match(esql_parser.NOT); } } - this.state = 179; + this.state = 183; this.match(esql_parser.NULL); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 191; + this.state = 195; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -830,7 +833,7 @@ export default class esql_parser extends Parser { } _prevctx = localctx; { - this.state = 189; + this.state = 193; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 7, this._ctx) ) { case 1: @@ -838,13 +841,13 @@ export default class esql_parser extends Parser { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 183; + this.state = 187; if (!(this.precpred(this._ctx, 4))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 4)"); } - this.state = 184; + this.state = 188; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.AND); - this.state = 185; + this.state = 189; (localctx as LogicalBinaryContext)._right = this.booleanExpression(5); } break; @@ -853,20 +856,20 @@ export default class esql_parser extends Parser { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 186; + this.state = 190; if (!(this.precpred(this._ctx, 3))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 3)"); } - this.state = 187; + this.state = 191; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.OR); - this.state = 188; + this.state = 192; (localctx as LogicalBinaryContext)._right = this.booleanExpression(4); } break; } } } - this.state = 193; + this.state = 197; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); } @@ -892,48 +895,48 @@ export default class esql_parser extends Parser { this.enterRule(localctx, 12, esql_parser.RULE_regexBooleanExpression); let _la: number; try { - this.state = 208; + this.state = 212; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 11, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 194; + this.state = 198; this.valueExpression(); - this.state = 196; + this.state = 200; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===48) { { - this.state = 195; + this.state = 199; this.match(esql_parser.NOT); } } - this.state = 198; + this.state = 202; localctx._kind = this.match(esql_parser.LIKE); - this.state = 199; + this.state = 203; localctx._pattern = this.string_(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 201; + this.state = 205; this.valueExpression(); - this.state = 203; + this.state = 207; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===48) { { - this.state = 202; + this.state = 206; this.match(esql_parser.NOT); } } - this.state = 205; + this.state = 209; localctx._kind = this.match(esql_parser.RLIKE); - this.state = 206; + this.state = 210; localctx._pattern = this.string_(); } break; @@ -958,14 +961,14 @@ export default class esql_parser extends Parser { let localctx: ValueExpressionContext = new ValueExpressionContext(this, this._ctx, this.state); this.enterRule(localctx, 14, esql_parser.RULE_valueExpression); try { - this.state = 215; + this.state = 219; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 12, this._ctx) ) { case 1: localctx = new ValueExpressionDefaultContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 210; + this.state = 214; this.operatorExpression(0); } break; @@ -973,11 +976,11 @@ export default class esql_parser extends Parser { localctx = new ComparisonContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 211; + this.state = 215; (localctx as ComparisonContext)._left = this.operatorExpression(0); - this.state = 212; + this.state = 216; this.comparisonOperator(); - this.state = 213; + this.state = 217; (localctx as ComparisonContext)._right = this.operatorExpression(0); } break; @@ -1017,7 +1020,7 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 221; + this.state = 225; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 13, this._ctx) ) { case 1: @@ -1026,7 +1029,7 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 218; + this.state = 222; this.primaryExpression(0); } break; @@ -1035,7 +1038,7 @@ export default class esql_parser extends Parser { localctx = new ArithmeticUnaryContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 219; + this.state = 223; (localctx as ArithmeticUnaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if(!(_la===63 || _la===64)) { @@ -1045,13 +1048,13 @@ export default class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 220; + this.state = 224; this.operatorExpression(3); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 231; + this.state = 235; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 15, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1061,7 +1064,7 @@ export default class esql_parser extends Parser { } _prevctx = localctx; { - this.state = 229; + this.state = 233; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 14, this._ctx) ) { case 1: @@ -1069,11 +1072,11 @@ export default class esql_parser extends Parser { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 223; + this.state = 227; if (!(this.precpred(this._ctx, 2))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 2)"); } - this.state = 224; + this.state = 228; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if(!(((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 7) !== 0))) { @@ -1083,7 +1086,7 @@ export default class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 225; + this.state = 229; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(3); } break; @@ -1092,11 +1095,11 @@ export default class esql_parser extends Parser { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 226; + this.state = 230; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 227; + this.state = 231; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if(!(_la===63 || _la===64)) { @@ -1106,14 +1109,14 @@ export default class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 228; + this.state = 232; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(2); } break; } } } - this.state = 233; + this.state = 237; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 15, this._ctx); } @@ -1152,7 +1155,7 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 242; + this.state = 246; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 16, this._ctx) ) { case 1: @@ -1161,7 +1164,7 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 235; + this.state = 239; this.constant(); } break; @@ -1170,7 +1173,7 @@ export default class esql_parser extends Parser { localctx = new DereferenceContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 236; + this.state = 240; this.qualifiedName(); } break; @@ -1179,7 +1182,7 @@ export default class esql_parser extends Parser { localctx = new FunctionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 237; + this.state = 241; this.functionExpression(); } break; @@ -1188,17 +1191,17 @@ export default class esql_parser extends Parser { localctx = new ParenthesizedExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 238; + this.state = 242; this.match(esql_parser.LP); - this.state = 239; + this.state = 243; this.booleanExpression(0); - this.state = 240; + this.state = 244; this.match(esql_parser.RP); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 249; + this.state = 253; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 17, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1211,18 +1214,18 @@ export default class esql_parser extends Parser { { localctx = new InlineCastContext(this, new PrimaryExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_primaryExpression); - this.state = 244; + this.state = 248; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 245; + this.state = 249; this.match(esql_parser.CAST_OP); - this.state = 246; + this.state = 250; this.dataType(); } } } - this.state = 251; + this.state = 255; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 17, this._ctx); } @@ -1250,16 +1253,16 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 252; + this.state = 256; this.identifier(); - this.state = 253; + this.state = 257; this.match(esql_parser.LP); - this.state = 263; + this.state = 267; this._errHandler.sync(this); switch (this._input.LA(1)) { case 65: { - this.state = 254; + this.state = 258; this.match(esql_parser.ASTERISK); } break; @@ -1280,21 +1283,21 @@ export default class esql_parser extends Parser { case 72: { { - this.state = 255; + this.state = 259; this.booleanExpression(0); - this.state = 260; + this.state = 264; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===38) { { { - this.state = 256; + this.state = 260; this.match(esql_parser.COMMA); - this.state = 257; + this.state = 261; this.booleanExpression(0); } } - this.state = 262; + this.state = 266; this._errHandler.sync(this); _la = this._input.LA(1); } @@ -1306,7 +1309,7 @@ export default class esql_parser extends Parser { default: break; } - this.state = 265; + this.state = 269; this.match(esql_parser.RP); } } @@ -1332,7 +1335,7 @@ export default class esql_parser extends Parser { localctx = new ToDataTypeContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 267; + this.state = 271; this.identifier(); } } @@ -1357,9 +1360,9 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 269; + this.state = 273; this.match(esql_parser.ROW); - this.state = 270; + this.state = 274; this.fields(); } } @@ -1385,23 +1388,23 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 272; + this.state = 276; this.field(); - this.state = 277; + this.state = 281; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 273; + this.state = 277; this.match(esql_parser.COMMA); - this.state = 274; + this.state = 278; this.field(); } } } - this.state = 279; + this.state = 283; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); } @@ -1426,24 +1429,24 @@ export default class esql_parser extends Parser { let localctx: FieldContext = new FieldContext(this, this._ctx, this.state); this.enterRule(localctx, 28, esql_parser.RULE_field); try { - this.state = 285; + this.state = 289; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 21, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 280; + this.state = 284; this.booleanExpression(0); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 281; + this.state = 285; this.qualifiedName(); - this.state = 282; + this.state = 286; this.match(esql_parser.ASSIGN); - this.state = 283; + this.state = 287; this.booleanExpression(0); } break; @@ -1471,34 +1474,34 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 287; + this.state = 291; this.match(esql_parser.FROM); - this.state = 288; - this.indexIdentifier(); - this.state = 293; + this.state = 292; + this.indexPattern(); + this.state = 297; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 289; + this.state = 293; this.match(esql_parser.COMMA); - this.state = 290; - this.indexIdentifier(); + this.state = 294; + this.indexPattern(); } } } - this.state = 295; + this.state = 299; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); } - this.state = 297; + this.state = 301; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 23, this._ctx) ) { case 1: { - this.state = 296; + this.state = 300; this.metadata(); } break; @@ -1520,14 +1523,89 @@ export default class esql_parser extends Parser { return localctx; } // @RuleVersion(0) - public indexIdentifier(): IndexIdentifierContext { - let localctx: IndexIdentifierContext = new IndexIdentifierContext(this, this._ctx, this.state); - this.enterRule(localctx, 32, esql_parser.RULE_indexIdentifier); + public indexPattern(): IndexPatternContext { + let localctx: IndexPatternContext = new IndexPatternContext(this, this._ctx, this.state); + this.enterRule(localctx, 32, esql_parser.RULE_indexPattern); + try { + this.state = 308; + this._errHandler.sync(this); + switch ( this._interp.adaptivePredict(this._input, 24, this._ctx) ) { + case 1: + this.enterOuterAlt(localctx, 1); + { + this.state = 303; + this.clusterString(); + this.state = 304; + this.match(esql_parser.COLON); + this.state = 305; + this.indexString(); + } + break; + case 2: + this.enterOuterAlt(localctx, 2); + { + this.state = 307; + this.indexString(); + } + break; + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public clusterString(): ClusterStringContext { + let localctx: ClusterStringContext = new ClusterStringContext(this, this._ctx, this.state); + this.enterRule(localctx, 34, esql_parser.RULE_clusterString); try { this.enterOuterAlt(localctx, 1); { - this.state = 299; - this.match(esql_parser.INDEX_UNQUOTED_IDENTIFIER); + this.state = 310; + this.match(esql_parser.UNQUOTED_SOURCE); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public indexString(): IndexStringContext { + let localctx: IndexStringContext = new IndexStringContext(this, this._ctx, this.state); + this.enterRule(localctx, 36, esql_parser.RULE_indexString); + let _la: number; + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 312; + _la = this._input.LA(1); + if(!(_la===25 || _la===30)) { + this._errHandler.recoverInline(this); + } + else { + this._errHandler.reportMatch(this); + this.consume(); + } } } catch (re) { @@ -1547,22 +1625,22 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public metadata(): MetadataContext { let localctx: MetadataContext = new MetadataContext(this, this._ctx, this.state); - this.enterRule(localctx, 34, esql_parser.RULE_metadata); + this.enterRule(localctx, 38, esql_parser.RULE_metadata); try { - this.state = 303; + this.state = 316; this._errHandler.sync(this); switch (this._input.LA(1)) { case 76: this.enterOuterAlt(localctx, 1); { - this.state = 301; + this.state = 314; this.metadataOption(); } break; case 69: this.enterOuterAlt(localctx, 2); { - this.state = 302; + this.state = 315; this.deprecated_metadata(); } break; @@ -1587,32 +1665,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public metadataOption(): MetadataOptionContext { let localctx: MetadataOptionContext = new MetadataOptionContext(this, this._ctx, this.state); - this.enterRule(localctx, 36, esql_parser.RULE_metadataOption); + this.enterRule(localctx, 40, esql_parser.RULE_metadataOption); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 305; + this.state = 318; this.match(esql_parser.METADATA); - this.state = 306; - this.indexIdentifier(); - this.state = 311; + this.state = 319; + this.match(esql_parser.UNQUOTED_SOURCE); + this.state = 324; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 307; + this.state = 320; this.match(esql_parser.COMMA); - this.state = 308; - this.indexIdentifier(); + this.state = 321; + this.match(esql_parser.UNQUOTED_SOURCE); } } } - this.state = 313; + this.state = 326; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); } } } @@ -1633,15 +1711,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public deprecated_metadata(): Deprecated_metadataContext { let localctx: Deprecated_metadataContext = new Deprecated_metadataContext(this, this._ctx, this.state); - this.enterRule(localctx, 38, esql_parser.RULE_deprecated_metadata); + this.enterRule(localctx, 42, esql_parser.RULE_deprecated_metadata); try { this.enterOuterAlt(localctx, 1); { - this.state = 314; + this.state = 327; this.match(esql_parser.OPENING_BRACKET); - this.state = 315; + this.state = 328; this.metadataOption(); - this.state = 316; + this.state = 329; this.match(esql_parser.CLOSING_BRACKET); } } @@ -1662,51 +1740,51 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public metricsCommand(): MetricsCommandContext { let localctx: MetricsCommandContext = new MetricsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 40, esql_parser.RULE_metricsCommand); + this.enterRule(localctx, 44, esql_parser.RULE_metricsCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 318; + this.state = 331; this.match(esql_parser.METRICS); - this.state = 319; - this.indexIdentifier(); - this.state = 324; + this.state = 332; + this.indexPattern(); + this.state = 337; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 320; + this.state = 333; this.match(esql_parser.COMMA); - this.state = 321; - this.indexIdentifier(); + this.state = 334; + this.indexPattern(); } } } - this.state = 326; + this.state = 339; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); } - this.state = 328; + this.state = 341; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 27, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 28, this._ctx) ) { case 1: { - this.state = 327; + this.state = 340; localctx._aggregates = this.fields(); } break; } - this.state = 332; + this.state = 345; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 28, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { case 1: { - this.state = 330; + this.state = 343; this.match(esql_parser.BY); - this.state = 331; + this.state = 344; localctx._grouping = this.fields(); } break; @@ -1730,13 +1808,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public evalCommand(): EvalCommandContext { let localctx: EvalCommandContext = new EvalCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 42, esql_parser.RULE_evalCommand); + this.enterRule(localctx, 46, esql_parser.RULE_evalCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 334; + this.state = 347; this.match(esql_parser.EVAL); - this.state = 335; + this.state = 348; this.fields(); } } @@ -1757,30 +1835,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public statsCommand(): StatsCommandContext { let localctx: StatsCommandContext = new StatsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 44, esql_parser.RULE_statsCommand); + this.enterRule(localctx, 48, esql_parser.RULE_statsCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 337; + this.state = 350; this.match(esql_parser.STATS); - this.state = 339; + this.state = 352; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { case 1: { - this.state = 338; + this.state = 351; localctx._stats = this.fields(); } break; } - this.state = 343; + this.state = 356; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { case 1: { - this.state = 341; + this.state = 354; this.match(esql_parser.BY); - this.state = 342; + this.state = 355; localctx._grouping = this.fields(); } break; @@ -1804,22 +1882,22 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public inlinestatsCommand(): InlinestatsCommandContext { let localctx: InlinestatsCommandContext = new InlinestatsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 46, esql_parser.RULE_inlinestatsCommand); + this.enterRule(localctx, 50, esql_parser.RULE_inlinestatsCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 345; + this.state = 358; this.match(esql_parser.INLINESTATS); - this.state = 346; + this.state = 359; localctx._stats = this.fields(); - this.state = 349; + this.state = 362; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 32, this._ctx) ) { case 1: { - this.state = 347; + this.state = 360; this.match(esql_parser.BY); - this.state = 348; + this.state = 361; localctx._grouping = this.fields(); } break; @@ -1843,30 +1921,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public qualifiedName(): QualifiedNameContext { let localctx: QualifiedNameContext = new QualifiedNameContext(this, this._ctx, this.state); - this.enterRule(localctx, 48, esql_parser.RULE_qualifiedName); + this.enterRule(localctx, 52, esql_parser.RULE_qualifiedName); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 351; + this.state = 364; this.identifier(); - this.state = 356; + this.state = 369; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 352; + this.state = 365; this.match(esql_parser.DOT); - this.state = 353; + this.state = 366; this.identifier(); } } } - this.state = 358; + this.state = 371; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); } } } @@ -1887,30 +1965,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public qualifiedNamePattern(): QualifiedNamePatternContext { let localctx: QualifiedNamePatternContext = new QualifiedNamePatternContext(this, this._ctx, this.state); - this.enterRule(localctx, 50, esql_parser.RULE_qualifiedNamePattern); + this.enterRule(localctx, 54, esql_parser.RULE_qualifiedNamePattern); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 359; + this.state = 372; this.identifierPattern(); - this.state = 364; + this.state = 377; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 360; + this.state = 373; this.match(esql_parser.DOT); - this.state = 361; + this.state = 374; this.identifierPattern(); } } } - this.state = 366; + this.state = 379; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); } } } @@ -1931,30 +2009,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public qualifiedNamePatterns(): QualifiedNamePatternsContext { let localctx: QualifiedNamePatternsContext = new QualifiedNamePatternsContext(this, this._ctx, this.state); - this.enterRule(localctx, 52, esql_parser.RULE_qualifiedNamePatterns); + this.enterRule(localctx, 56, esql_parser.RULE_qualifiedNamePatterns); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 367; + this.state = 380; this.qualifiedNamePattern(); - this.state = 372; + this.state = 385; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 368; + this.state = 381; this.match(esql_parser.COMMA); - this.state = 369; + this.state = 382; this.qualifiedNamePattern(); } } } - this.state = 374; + this.state = 387; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); } } } @@ -1975,12 +2053,12 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public identifier(): IdentifierContext { let localctx: IdentifierContext = new IdentifierContext(this, this._ctx, this.state); - this.enterRule(localctx, 54, esql_parser.RULE_identifier); + this.enterRule(localctx, 58, esql_parser.RULE_identifier); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 375; + this.state = 388; _la = this._input.LA(1); if(!(_la===71 || _la===72)) { this._errHandler.recoverInline(this); @@ -2008,11 +2086,11 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public identifierPattern(): IdentifierPatternContext { let localctx: IdentifierPatternContext = new IdentifierPatternContext(this, this._ctx, this.state); - this.enterRule(localctx, 56, esql_parser.RULE_identifierPattern); + this.enterRule(localctx, 60, esql_parser.RULE_identifierPattern); try { this.enterOuterAlt(localctx, 1); { - this.state = 377; + this.state = 390; this.match(esql_parser.ID_PATTERN); } } @@ -2033,17 +2111,17 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public constant(): ConstantContext { let localctx: ConstantContext = new ConstantContext(this, this._ctx, this.state); - this.enterRule(localctx, 58, esql_parser.RULE_constant); + this.enterRule(localctx, 62, esql_parser.RULE_constant); let _la: number; try { - this.state = 421; + this.state = 434; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 38, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 39, this._ctx) ) { case 1: localctx = new NullLiteralContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 379; + this.state = 392; this.match(esql_parser.NULL); } break; @@ -2051,9 +2129,9 @@ export default class esql_parser extends Parser { localctx = new QualifiedIntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 380; + this.state = 393; this.integerValue(); - this.state = 381; + this.state = 394; this.match(esql_parser.UNQUOTED_IDENTIFIER); } break; @@ -2061,7 +2139,7 @@ export default class esql_parser extends Parser { localctx = new DecimalLiteralContext(this, localctx); this.enterOuterAlt(localctx, 3); { - this.state = 383; + this.state = 396; this.decimalValue(); } break; @@ -2069,7 +2147,7 @@ export default class esql_parser extends Parser { localctx = new IntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 4); { - this.state = 384; + this.state = 397; this.integerValue(); } break; @@ -2077,7 +2155,7 @@ export default class esql_parser extends Parser { localctx = new BooleanLiteralContext(this, localctx); this.enterOuterAlt(localctx, 5); { - this.state = 385; + this.state = 398; this.booleanValue(); } break; @@ -2085,7 +2163,7 @@ export default class esql_parser extends Parser { localctx = new InputParamsContext(this, localctx); this.enterOuterAlt(localctx, 6); { - this.state = 386; + this.state = 399; this.params(); } break; @@ -2093,7 +2171,7 @@ export default class esql_parser extends Parser { localctx = new StringLiteralContext(this, localctx); this.enterOuterAlt(localctx, 7); { - this.state = 387; + this.state = 400; this.string_(); } break; @@ -2101,27 +2179,27 @@ export default class esql_parser extends Parser { localctx = new NumericArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 8); { - this.state = 388; + this.state = 401; this.match(esql_parser.OPENING_BRACKET); - this.state = 389; + this.state = 402; this.numericValue(); - this.state = 394; + this.state = 407; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===38) { { { - this.state = 390; + this.state = 403; this.match(esql_parser.COMMA); - this.state = 391; + this.state = 404; this.numericValue(); } } - this.state = 396; + this.state = 409; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 397; + this.state = 410; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2129,27 +2207,27 @@ export default class esql_parser extends Parser { localctx = new BooleanArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 9); { - this.state = 399; + this.state = 412; this.match(esql_parser.OPENING_BRACKET); - this.state = 400; + this.state = 413; this.booleanValue(); - this.state = 405; + this.state = 418; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===38) { { { - this.state = 401; + this.state = 414; this.match(esql_parser.COMMA); - this.state = 402; + this.state = 415; this.booleanValue(); } } - this.state = 407; + this.state = 420; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 408; + this.state = 421; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2157,27 +2235,27 @@ export default class esql_parser extends Parser { localctx = new StringArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 10); { - this.state = 410; + this.state = 423; this.match(esql_parser.OPENING_BRACKET); - this.state = 411; + this.state = 424; this.string_(); - this.state = 416; + this.state = 429; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===38) { { { - this.state = 412; + this.state = 425; this.match(esql_parser.COMMA); - this.state = 413; + this.state = 426; this.string_(); } } - this.state = 418; + this.state = 431; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 419; + this.state = 432; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2200,16 +2278,16 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public params(): ParamsContext { let localctx: ParamsContext = new ParamsContext(this, this._ctx, this.state); - this.enterRule(localctx, 60, esql_parser.RULE_params); + this.enterRule(localctx, 64, esql_parser.RULE_params); try { - this.state = 425; + this.state = 438; this._errHandler.sync(this); switch (this._input.LA(1)) { case 52: localctx = new InputParamContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 423; + this.state = 436; this.match(esql_parser.PARAM); } break; @@ -2217,7 +2295,7 @@ export default class esql_parser extends Parser { localctx = new InputNamedOrPositionalParamContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 424; + this.state = 437; this.match(esql_parser.NAMED_OR_POSITIONAL_PARAM); } break; @@ -2242,13 +2320,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public limitCommand(): LimitCommandContext { let localctx: LimitCommandContext = new LimitCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 62, esql_parser.RULE_limitCommand); + this.enterRule(localctx, 66, esql_parser.RULE_limitCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 427; + this.state = 440; this.match(esql_parser.LIMIT); - this.state = 428; + this.state = 441; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2269,32 +2347,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public sortCommand(): SortCommandContext { let localctx: SortCommandContext = new SortCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 64, esql_parser.RULE_sortCommand); + this.enterRule(localctx, 68, esql_parser.RULE_sortCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 430; + this.state = 443; this.match(esql_parser.SORT); - this.state = 431; + this.state = 444; this.orderExpression(); - this.state = 436; + this.state = 449; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 41, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 432; + this.state = 445; this.match(esql_parser.COMMA); - this.state = 433; + this.state = 446; this.orderExpression(); } } } - this.state = 438; + this.state = 451; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 41, this._ctx); } } } @@ -2315,19 +2393,19 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public orderExpression(): OrderExpressionContext { let localctx: OrderExpressionContext = new OrderExpressionContext(this, this._ctx, this.state); - this.enterRule(localctx, 66, esql_parser.RULE_orderExpression); + this.enterRule(localctx, 70, esql_parser.RULE_orderExpression); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 439; + this.state = 452; this.booleanExpression(0); - this.state = 441; + this.state = 454; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 41, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 42, this._ctx) ) { case 1: { - this.state = 440; + this.state = 453; localctx._ordering = this._input.LT(1); _la = this._input.LA(1); if(!(_la===35 || _la===39)) { @@ -2340,14 +2418,14 @@ export default class esql_parser extends Parser { } break; } - this.state = 445; + this.state = 458; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 42, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 43, this._ctx) ) { case 1: { - this.state = 443; + this.state = 456; this.match(esql_parser.NULLS); - this.state = 444; + this.state = 457; localctx._nullOrdering = this._input.LT(1); _la = this._input.LA(1); if(!(_la===42 || _la===43)) { @@ -2379,13 +2457,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public keepCommand(): KeepCommandContext { let localctx: KeepCommandContext = new KeepCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 68, esql_parser.RULE_keepCommand); + this.enterRule(localctx, 72, esql_parser.RULE_keepCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 447; + this.state = 460; this.match(esql_parser.KEEP); - this.state = 448; + this.state = 461; this.qualifiedNamePatterns(); } } @@ -2406,13 +2484,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public dropCommand(): DropCommandContext { let localctx: DropCommandContext = new DropCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 70, esql_parser.RULE_dropCommand); + this.enterRule(localctx, 74, esql_parser.RULE_dropCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 450; + this.state = 463; this.match(esql_parser.DROP); - this.state = 451; + this.state = 464; this.qualifiedNamePatterns(); } } @@ -2433,32 +2511,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public renameCommand(): RenameCommandContext { let localctx: RenameCommandContext = new RenameCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 72, esql_parser.RULE_renameCommand); + this.enterRule(localctx, 76, esql_parser.RULE_renameCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 453; + this.state = 466; this.match(esql_parser.RENAME); - this.state = 454; + this.state = 467; this.renameClause(); - this.state = 459; + this.state = 472; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 43, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 44, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 455; + this.state = 468; this.match(esql_parser.COMMA); - this.state = 456; + this.state = 469; this.renameClause(); } } } - this.state = 461; + this.state = 474; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 43, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 44, this._ctx); } } } @@ -2479,15 +2557,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public renameClause(): RenameClauseContext { let localctx: RenameClauseContext = new RenameClauseContext(this, this._ctx, this.state); - this.enterRule(localctx, 74, esql_parser.RULE_renameClause); + this.enterRule(localctx, 78, esql_parser.RULE_renameClause); try { this.enterOuterAlt(localctx, 1); { - this.state = 462; + this.state = 475; localctx._oldName = this.qualifiedNamePattern(); - this.state = 463; + this.state = 476; this.match(esql_parser.AS); - this.state = 464; + this.state = 477; localctx._newName = this.qualifiedNamePattern(); } } @@ -2508,22 +2586,22 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public dissectCommand(): DissectCommandContext { let localctx: DissectCommandContext = new DissectCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 76, esql_parser.RULE_dissectCommand); + this.enterRule(localctx, 80, esql_parser.RULE_dissectCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 466; + this.state = 479; this.match(esql_parser.DISSECT); - this.state = 467; + this.state = 480; this.primaryExpression(0); - this.state = 468; + this.state = 481; this.string_(); - this.state = 470; + this.state = 483; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 44, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 45, this._ctx) ) { case 1: { - this.state = 469; + this.state = 482; this.commandOptions(); } break; @@ -2547,15 +2625,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public grokCommand(): GrokCommandContext { let localctx: GrokCommandContext = new GrokCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 78, esql_parser.RULE_grokCommand); + this.enterRule(localctx, 82, esql_parser.RULE_grokCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 472; + this.state = 485; this.match(esql_parser.GROK); - this.state = 473; + this.state = 486; this.primaryExpression(0); - this.state = 474; + this.state = 487; this.string_(); } } @@ -2576,13 +2654,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public mvExpandCommand(): MvExpandCommandContext { let localctx: MvExpandCommandContext = new MvExpandCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 80, esql_parser.RULE_mvExpandCommand); + this.enterRule(localctx, 84, esql_parser.RULE_mvExpandCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 476; + this.state = 489; this.match(esql_parser.MV_EXPAND); - this.state = 477; + this.state = 490; this.qualifiedName(); } } @@ -2603,30 +2681,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public commandOptions(): CommandOptionsContext { let localctx: CommandOptionsContext = new CommandOptionsContext(this, this._ctx, this.state); - this.enterRule(localctx, 82, esql_parser.RULE_commandOptions); + this.enterRule(localctx, 86, esql_parser.RULE_commandOptions); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 479; + this.state = 492; this.commandOption(); - this.state = 484; + this.state = 497; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 46, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 480; + this.state = 493; this.match(esql_parser.COMMA); - this.state = 481; + this.state = 494; this.commandOption(); } } } - this.state = 486; + this.state = 499; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 46, this._ctx); } } } @@ -2647,15 +2725,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public commandOption(): CommandOptionContext { let localctx: CommandOptionContext = new CommandOptionContext(this, this._ctx, this.state); - this.enterRule(localctx, 84, esql_parser.RULE_commandOption); + this.enterRule(localctx, 88, esql_parser.RULE_commandOption); try { this.enterOuterAlt(localctx, 1); { - this.state = 487; + this.state = 500; this.identifier(); - this.state = 488; + this.state = 501; this.match(esql_parser.ASSIGN); - this.state = 489; + this.state = 502; this.constant(); } } @@ -2676,12 +2754,12 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public booleanValue(): BooleanValueContext { let localctx: BooleanValueContext = new BooleanValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 86, esql_parser.RULE_booleanValue); + this.enterRule(localctx, 90, esql_parser.RULE_booleanValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 491; + this.state = 504; _la = this._input.LA(1); if(!(_la===41 || _la===55)) { this._errHandler.recoverInline(this); @@ -2709,22 +2787,22 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public numericValue(): NumericValueContext { let localctx: NumericValueContext = new NumericValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 88, esql_parser.RULE_numericValue); + this.enterRule(localctx, 92, esql_parser.RULE_numericValue); try { - this.state = 495; + this.state = 508; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 46, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 47, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 493; + this.state = 506; this.decimalValue(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 494; + this.state = 507; this.integerValue(); } break; @@ -2747,17 +2825,17 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public decimalValue(): DecimalValueContext { let localctx: DecimalValueContext = new DecimalValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 90, esql_parser.RULE_decimalValue); + this.enterRule(localctx, 94, esql_parser.RULE_decimalValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 498; + this.state = 511; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===63 || _la===64) { { - this.state = 497; + this.state = 510; _la = this._input.LA(1); if(!(_la===63 || _la===64)) { this._errHandler.recoverInline(this); @@ -2769,7 +2847,7 @@ export default class esql_parser extends Parser { } } - this.state = 500; + this.state = 513; this.match(esql_parser.DECIMAL_LITERAL); } } @@ -2790,17 +2868,17 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public integerValue(): IntegerValueContext { let localctx: IntegerValueContext = new IntegerValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 92, esql_parser.RULE_integerValue); + this.enterRule(localctx, 96, esql_parser.RULE_integerValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 503; + this.state = 516; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===63 || _la===64) { { - this.state = 502; + this.state = 515; _la = this._input.LA(1); if(!(_la===63 || _la===64)) { this._errHandler.recoverInline(this); @@ -2812,7 +2890,7 @@ export default class esql_parser extends Parser { } } - this.state = 505; + this.state = 518; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2833,11 +2911,11 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public string_(): StringContext { let localctx: StringContext = new StringContext(this, this._ctx, this.state); - this.enterRule(localctx, 94, esql_parser.RULE_string); + this.enterRule(localctx, 98, esql_parser.RULE_string); try { this.enterOuterAlt(localctx, 1); { - this.state = 507; + this.state = 520; this.match(esql_parser.QUOTED_STRING); } } @@ -2858,12 +2936,12 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public comparisonOperator(): ComparisonOperatorContext { let localctx: ComparisonOperatorContext = new ComparisonOperatorContext(this, this._ctx, this.state); - this.enterRule(localctx, 96, esql_parser.RULE_comparisonOperator); + this.enterRule(localctx, 100, esql_parser.RULE_comparisonOperator); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 509; + this.state = 522; _la = this._input.LA(1); if(!(((((_la - 56)) & ~0x1F) === 0 && ((1 << (_la - 56)) & 125) !== 0))) { this._errHandler.recoverInline(this); @@ -2891,13 +2969,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public explainCommand(): ExplainCommandContext { let localctx: ExplainCommandContext = new ExplainCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 98, esql_parser.RULE_explainCommand); + this.enterRule(localctx, 102, esql_parser.RULE_explainCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 511; + this.state = 524; this.match(esql_parser.EXPLAIN); - this.state = 512; + this.state = 525; this.subqueryExpression(); } } @@ -2918,15 +2996,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public subqueryExpression(): SubqueryExpressionContext { let localctx: SubqueryExpressionContext = new SubqueryExpressionContext(this, this._ctx, this.state); - this.enterRule(localctx, 100, esql_parser.RULE_subqueryExpression); + this.enterRule(localctx, 104, esql_parser.RULE_subqueryExpression); try { this.enterOuterAlt(localctx, 1); { - this.state = 514; + this.state = 527; this.match(esql_parser.OPENING_BRACKET); - this.state = 515; + this.state = 528; this.query(0); - this.state = 516; + this.state = 529; this.match(esql_parser.CLOSING_BRACKET); } } @@ -2947,14 +3025,14 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public showCommand(): ShowCommandContext { let localctx: ShowCommandContext = new ShowCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 102, esql_parser.RULE_showCommand); + this.enterRule(localctx, 106, esql_parser.RULE_showCommand); try { localctx = new ShowInfoContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 518; + this.state = 531; this.match(esql_parser.SHOW); - this.state = 519; + this.state = 532; this.match(esql_parser.INFO); } } @@ -2975,14 +3053,14 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public metaCommand(): MetaCommandContext { let localctx: MetaCommandContext = new MetaCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 104, esql_parser.RULE_metaCommand); + this.enterRule(localctx, 108, esql_parser.RULE_metaCommand); try { localctx = new MetaFunctionsContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 521; + this.state = 534; this.match(esql_parser.META); - this.state = 522; + this.state = 535; this.match(esql_parser.FUNCTIONS); } } @@ -3003,53 +3081,53 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public enrichCommand(): EnrichCommandContext { let localctx: EnrichCommandContext = new EnrichCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 106, esql_parser.RULE_enrichCommand); + this.enterRule(localctx, 110, esql_parser.RULE_enrichCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 524; + this.state = 537; this.match(esql_parser.ENRICH); - this.state = 525; + this.state = 538; localctx._policyName = this.match(esql_parser.ENRICH_POLICY_NAME); - this.state = 528; + this.state = 541; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 49, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 50, this._ctx) ) { case 1: { - this.state = 526; + this.state = 539; this.match(esql_parser.ON); - this.state = 527; + this.state = 540; localctx._matchField = this.qualifiedNamePattern(); } break; } - this.state = 539; + this.state = 552; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 51, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 52, this._ctx) ) { case 1: { - this.state = 530; + this.state = 543; this.match(esql_parser.WITH); - this.state = 531; + this.state = 544; this.enrichWithClause(); - this.state = 536; + this.state = 549; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 50, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 51, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 532; + this.state = 545; this.match(esql_parser.COMMA); - this.state = 533; + this.state = 546; this.enrichWithClause(); } } } - this.state = 538; + this.state = 551; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 50, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 51, this._ctx); } } break; @@ -3073,23 +3151,23 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public enrichWithClause(): EnrichWithClauseContext { let localctx: EnrichWithClauseContext = new EnrichWithClauseContext(this, this._ctx, this.state); - this.enterRule(localctx, 108, esql_parser.RULE_enrichWithClause); + this.enterRule(localctx, 112, esql_parser.RULE_enrichWithClause); try { this.enterOuterAlt(localctx, 1); { - this.state = 544; + this.state = 557; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 52, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 53, this._ctx) ) { case 1: { - this.state = 541; + this.state = 554; localctx._newName = this.qualifiedNamePattern(); - this.state = 542; + this.state = 555; this.match(esql_parser.ASSIGN); } break; } - this.state = 546; + this.state = 559; localctx._enrichField = this.qualifiedNamePattern(); } } @@ -3110,17 +3188,17 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public lookupCommand(): LookupCommandContext { let localctx: LookupCommandContext = new LookupCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 110, esql_parser.RULE_lookupCommand); + this.enterRule(localctx, 114, esql_parser.RULE_lookupCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 548; + this.state = 561; this.match(esql_parser.LOOKUP); - this.state = 549; - localctx._tableName = this.match(esql_parser.INDEX_UNQUOTED_IDENTIFIER); - this.state = 550; + this.state = 562; + localctx._tableName = this.indexPattern(); + this.state = 563; this.match(esql_parser.ON); - this.state = 551; + this.state = 564; localctx._matchFields = this.qualifiedNamePatterns(); } } @@ -3185,7 +3263,7 @@ export default class esql_parser extends Parser { return true; } - public static readonly _serializedATN: number[] = [4,1,124,554,2,0,7,0, + public static readonly _serializedATN: number[] = [4,1,124,567,2,0,7,0, 2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9, 2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2, 17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24, @@ -3193,179 +3271,183 @@ export default class esql_parser extends Parser { 31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38, 2,39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,45,2, 46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,52,7,52,2,53, - 7,53,2,54,7,54,2,55,7,55,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,5,1,122,8, - 1,10,1,12,1,125,9,1,1,2,1,2,1,2,1,2,1,2,1,2,3,2,133,8,2,1,3,1,3,1,3,1,3, - 1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,3,3,149,8,3,1,4,1,4,1,4,1,5,1,5, - 1,5,1,5,1,5,1,5,1,5,3,5,161,8,5,1,5,1,5,1,5,1,5,1,5,5,5,168,8,5,10,5,12, - 5,171,9,5,1,5,1,5,1,5,1,5,1,5,3,5,178,8,5,1,5,1,5,3,5,182,8,5,1,5,1,5,1, - 5,1,5,1,5,1,5,5,5,190,8,5,10,5,12,5,193,9,5,1,6,1,6,3,6,197,8,6,1,6,1,6, - 1,6,1,6,1,6,3,6,204,8,6,1,6,1,6,1,6,3,6,209,8,6,1,7,1,7,1,7,1,7,1,7,3,7, - 216,8,7,1,8,1,8,1,8,1,8,3,8,222,8,8,1,8,1,8,1,8,1,8,1,8,1,8,5,8,230,8,8, - 10,8,12,8,233,9,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,3,9,243,8,9,1,9,1,9,1, - 9,5,9,248,8,9,10,9,12,9,251,9,9,1,10,1,10,1,10,1,10,1,10,1,10,5,10,259, - 8,10,10,10,12,10,262,9,10,3,10,264,8,10,1,10,1,10,1,11,1,11,1,12,1,12,1, - 12,1,13,1,13,1,13,5,13,276,8,13,10,13,12,13,279,9,13,1,14,1,14,1,14,1,14, - 1,14,3,14,286,8,14,1,15,1,15,1,15,1,15,5,15,292,8,15,10,15,12,15,295,9, - 15,1,15,3,15,298,8,15,1,16,1,16,1,17,1,17,3,17,304,8,17,1,18,1,18,1,18, - 1,18,5,18,310,8,18,10,18,12,18,313,9,18,1,19,1,19,1,19,1,19,1,20,1,20,1, - 20,1,20,5,20,323,8,20,10,20,12,20,326,9,20,1,20,3,20,329,8,20,1,20,1,20, - 3,20,333,8,20,1,21,1,21,1,21,1,22,1,22,3,22,340,8,22,1,22,1,22,3,22,344, - 8,22,1,23,1,23,1,23,1,23,3,23,350,8,23,1,24,1,24,1,24,5,24,355,8,24,10, - 24,12,24,358,9,24,1,25,1,25,1,25,5,25,363,8,25,10,25,12,25,366,9,25,1,26, - 1,26,1,26,5,26,371,8,26,10,26,12,26,374,9,26,1,27,1,27,1,28,1,28,1,29,1, - 29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,5,29,393,8,29, - 10,29,12,29,396,9,29,1,29,1,29,1,29,1,29,1,29,1,29,5,29,404,8,29,10,29, - 12,29,407,9,29,1,29,1,29,1,29,1,29,1,29,1,29,5,29,415,8,29,10,29,12,29, - 418,9,29,1,29,1,29,3,29,422,8,29,1,30,1,30,3,30,426,8,30,1,31,1,31,1,31, - 1,32,1,32,1,32,1,32,5,32,435,8,32,10,32,12,32,438,9,32,1,33,1,33,3,33,442, - 8,33,1,33,1,33,3,33,446,8,33,1,34,1,34,1,34,1,35,1,35,1,35,1,36,1,36,1, - 36,1,36,5,36,458,8,36,10,36,12,36,461,9,36,1,37,1,37,1,37,1,37,1,38,1,38, - 1,38,1,38,3,38,471,8,38,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,41,1,41,1, - 41,5,41,483,8,41,10,41,12,41,486,9,41,1,42,1,42,1,42,1,42,1,43,1,43,1,44, - 1,44,3,44,496,8,44,1,45,3,45,499,8,45,1,45,1,45,1,46,3,46,504,8,46,1,46, - 1,46,1,47,1,47,1,48,1,48,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,51,1,51,1, - 51,1,52,1,52,1,52,1,53,1,53,1,53,1,53,3,53,529,8,53,1,53,1,53,1,53,1,53, - 5,53,535,8,53,10,53,12,53,538,9,53,3,53,540,8,53,1,54,1,54,1,54,3,54,545, - 8,54,1,54,1,54,1,55,1,55,1,55,1,55,1,55,1,55,0,4,2,10,16,18,56,0,2,4,6, - 8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54, - 56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102, - 104,106,108,110,0,7,1,0,63,64,1,0,65,67,1,0,71,72,2,0,35,35,39,39,1,0,42, - 43,2,0,41,41,55,55,2,0,56,56,58,62,580,0,112,1,0,0,0,2,115,1,0,0,0,4,132, - 1,0,0,0,6,148,1,0,0,0,8,150,1,0,0,0,10,181,1,0,0,0,12,208,1,0,0,0,14,215, - 1,0,0,0,16,221,1,0,0,0,18,242,1,0,0,0,20,252,1,0,0,0,22,267,1,0,0,0,24, - 269,1,0,0,0,26,272,1,0,0,0,28,285,1,0,0,0,30,287,1,0,0,0,32,299,1,0,0,0, - 34,303,1,0,0,0,36,305,1,0,0,0,38,314,1,0,0,0,40,318,1,0,0,0,42,334,1,0, - 0,0,44,337,1,0,0,0,46,345,1,0,0,0,48,351,1,0,0,0,50,359,1,0,0,0,52,367, - 1,0,0,0,54,375,1,0,0,0,56,377,1,0,0,0,58,421,1,0,0,0,60,425,1,0,0,0,62, - 427,1,0,0,0,64,430,1,0,0,0,66,439,1,0,0,0,68,447,1,0,0,0,70,450,1,0,0,0, - 72,453,1,0,0,0,74,462,1,0,0,0,76,466,1,0,0,0,78,472,1,0,0,0,80,476,1,0, - 0,0,82,479,1,0,0,0,84,487,1,0,0,0,86,491,1,0,0,0,88,495,1,0,0,0,90,498, - 1,0,0,0,92,503,1,0,0,0,94,507,1,0,0,0,96,509,1,0,0,0,98,511,1,0,0,0,100, - 514,1,0,0,0,102,518,1,0,0,0,104,521,1,0,0,0,106,524,1,0,0,0,108,544,1,0, - 0,0,110,548,1,0,0,0,112,113,3,2,1,0,113,114,5,0,0,1,114,1,1,0,0,0,115,116, - 6,1,-1,0,116,117,3,4,2,0,117,123,1,0,0,0,118,119,10,1,0,0,119,120,5,29, - 0,0,120,122,3,6,3,0,121,118,1,0,0,0,122,125,1,0,0,0,123,121,1,0,0,0,123, - 124,1,0,0,0,124,3,1,0,0,0,125,123,1,0,0,0,126,133,3,98,49,0,127,133,3,30, - 15,0,128,133,3,24,12,0,129,133,3,40,20,0,130,133,3,102,51,0,131,133,3,104, - 52,0,132,126,1,0,0,0,132,127,1,0,0,0,132,128,1,0,0,0,132,129,1,0,0,0,132, - 130,1,0,0,0,132,131,1,0,0,0,133,5,1,0,0,0,134,149,3,42,21,0,135,149,3,46, - 23,0,136,149,3,62,31,0,137,149,3,110,55,0,138,149,3,68,34,0,139,149,3,64, - 32,0,140,149,3,44,22,0,141,149,3,8,4,0,142,149,3,70,35,0,143,149,3,72,36, - 0,144,149,3,76,38,0,145,149,3,78,39,0,146,149,3,106,53,0,147,149,3,80,40, - 0,148,134,1,0,0,0,148,135,1,0,0,0,148,136,1,0,0,0,148,137,1,0,0,0,148,138, - 1,0,0,0,148,139,1,0,0,0,148,140,1,0,0,0,148,141,1,0,0,0,148,142,1,0,0,0, - 148,143,1,0,0,0,148,144,1,0,0,0,148,145,1,0,0,0,148,146,1,0,0,0,148,147, - 1,0,0,0,149,7,1,0,0,0,150,151,5,20,0,0,151,152,3,10,5,0,152,9,1,0,0,0,153, - 154,6,5,-1,0,154,155,5,48,0,0,155,182,3,10,5,7,156,182,3,14,7,0,157,182, - 3,12,6,0,158,160,3,14,7,0,159,161,5,48,0,0,160,159,1,0,0,0,160,161,1,0, - 0,0,161,162,1,0,0,0,162,163,5,45,0,0,163,164,5,44,0,0,164,169,3,14,7,0, - 165,166,5,38,0,0,166,168,3,14,7,0,167,165,1,0,0,0,168,171,1,0,0,0,169,167, - 1,0,0,0,169,170,1,0,0,0,170,172,1,0,0,0,171,169,1,0,0,0,172,173,5,54,0, - 0,173,182,1,0,0,0,174,175,3,14,7,0,175,177,5,46,0,0,176,178,5,48,0,0,177, - 176,1,0,0,0,177,178,1,0,0,0,178,179,1,0,0,0,179,180,5,49,0,0,180,182,1, - 0,0,0,181,153,1,0,0,0,181,156,1,0,0,0,181,157,1,0,0,0,181,158,1,0,0,0,181, - 174,1,0,0,0,182,191,1,0,0,0,183,184,10,4,0,0,184,185,5,34,0,0,185,190,3, - 10,5,5,186,187,10,3,0,0,187,188,5,51,0,0,188,190,3,10,5,4,189,183,1,0,0, - 0,189,186,1,0,0,0,190,193,1,0,0,0,191,189,1,0,0,0,191,192,1,0,0,0,192,11, - 1,0,0,0,193,191,1,0,0,0,194,196,3,14,7,0,195,197,5,48,0,0,196,195,1,0,0, - 0,196,197,1,0,0,0,197,198,1,0,0,0,198,199,5,47,0,0,199,200,3,94,47,0,200, - 209,1,0,0,0,201,203,3,14,7,0,202,204,5,48,0,0,203,202,1,0,0,0,203,204,1, - 0,0,0,204,205,1,0,0,0,205,206,5,53,0,0,206,207,3,94,47,0,207,209,1,0,0, - 0,208,194,1,0,0,0,208,201,1,0,0,0,209,13,1,0,0,0,210,216,3,16,8,0,211,212, - 3,16,8,0,212,213,3,96,48,0,213,214,3,16,8,0,214,216,1,0,0,0,215,210,1,0, - 0,0,215,211,1,0,0,0,216,15,1,0,0,0,217,218,6,8,-1,0,218,222,3,18,9,0,219, - 220,7,0,0,0,220,222,3,16,8,3,221,217,1,0,0,0,221,219,1,0,0,0,222,231,1, - 0,0,0,223,224,10,2,0,0,224,225,7,1,0,0,225,230,3,16,8,3,226,227,10,1,0, - 0,227,228,7,0,0,0,228,230,3,16,8,2,229,223,1,0,0,0,229,226,1,0,0,0,230, - 233,1,0,0,0,231,229,1,0,0,0,231,232,1,0,0,0,232,17,1,0,0,0,233,231,1,0, - 0,0,234,235,6,9,-1,0,235,243,3,58,29,0,236,243,3,48,24,0,237,243,3,20,10, - 0,238,239,5,44,0,0,239,240,3,10,5,0,240,241,5,54,0,0,241,243,1,0,0,0,242, - 234,1,0,0,0,242,236,1,0,0,0,242,237,1,0,0,0,242,238,1,0,0,0,243,249,1,0, - 0,0,244,245,10,1,0,0,245,246,5,37,0,0,246,248,3,22,11,0,247,244,1,0,0,0, - 248,251,1,0,0,0,249,247,1,0,0,0,249,250,1,0,0,0,250,19,1,0,0,0,251,249, - 1,0,0,0,252,253,3,54,27,0,253,263,5,44,0,0,254,264,5,65,0,0,255,260,3,10, - 5,0,256,257,5,38,0,0,257,259,3,10,5,0,258,256,1,0,0,0,259,262,1,0,0,0,260, - 258,1,0,0,0,260,261,1,0,0,0,261,264,1,0,0,0,262,260,1,0,0,0,263,254,1,0, - 0,0,263,255,1,0,0,0,263,264,1,0,0,0,264,265,1,0,0,0,265,266,5,54,0,0,266, - 21,1,0,0,0,267,268,3,54,27,0,268,23,1,0,0,0,269,270,5,16,0,0,270,271,3, - 26,13,0,271,25,1,0,0,0,272,277,3,28,14,0,273,274,5,38,0,0,274,276,3,28, - 14,0,275,273,1,0,0,0,276,279,1,0,0,0,277,275,1,0,0,0,277,278,1,0,0,0,278, - 27,1,0,0,0,279,277,1,0,0,0,280,286,3,10,5,0,281,282,3,48,24,0,282,283,5, - 36,0,0,283,284,3,10,5,0,284,286,1,0,0,0,285,280,1,0,0,0,285,281,1,0,0,0, - 286,29,1,0,0,0,287,288,5,6,0,0,288,293,3,32,16,0,289,290,5,38,0,0,290,292, - 3,32,16,0,291,289,1,0,0,0,292,295,1,0,0,0,293,291,1,0,0,0,293,294,1,0,0, - 0,294,297,1,0,0,0,295,293,1,0,0,0,296,298,3,34,17,0,297,296,1,0,0,0,297, - 298,1,0,0,0,298,31,1,0,0,0,299,300,5,25,0,0,300,33,1,0,0,0,301,304,3,36, - 18,0,302,304,3,38,19,0,303,301,1,0,0,0,303,302,1,0,0,0,304,35,1,0,0,0,305, - 306,5,76,0,0,306,311,3,32,16,0,307,308,5,38,0,0,308,310,3,32,16,0,309,307, - 1,0,0,0,310,313,1,0,0,0,311,309,1,0,0,0,311,312,1,0,0,0,312,37,1,0,0,0, - 313,311,1,0,0,0,314,315,5,69,0,0,315,316,3,36,18,0,316,317,5,70,0,0,317, - 39,1,0,0,0,318,319,5,13,0,0,319,324,3,32,16,0,320,321,5,38,0,0,321,323, - 3,32,16,0,322,320,1,0,0,0,323,326,1,0,0,0,324,322,1,0,0,0,324,325,1,0,0, - 0,325,328,1,0,0,0,326,324,1,0,0,0,327,329,3,26,13,0,328,327,1,0,0,0,328, - 329,1,0,0,0,329,332,1,0,0,0,330,331,5,33,0,0,331,333,3,26,13,0,332,330, - 1,0,0,0,332,333,1,0,0,0,333,41,1,0,0,0,334,335,5,4,0,0,335,336,3,26,13, - 0,336,43,1,0,0,0,337,339,5,19,0,0,338,340,3,26,13,0,339,338,1,0,0,0,339, - 340,1,0,0,0,340,343,1,0,0,0,341,342,5,33,0,0,342,344,3,26,13,0,343,341, - 1,0,0,0,343,344,1,0,0,0,344,45,1,0,0,0,345,346,5,8,0,0,346,349,3,26,13, - 0,347,348,5,33,0,0,348,350,3,26,13,0,349,347,1,0,0,0,349,350,1,0,0,0,350, - 47,1,0,0,0,351,356,3,54,27,0,352,353,5,40,0,0,353,355,3,54,27,0,354,352, - 1,0,0,0,355,358,1,0,0,0,356,354,1,0,0,0,356,357,1,0,0,0,357,49,1,0,0,0, - 358,356,1,0,0,0,359,364,3,56,28,0,360,361,5,40,0,0,361,363,3,56,28,0,362, - 360,1,0,0,0,363,366,1,0,0,0,364,362,1,0,0,0,364,365,1,0,0,0,365,51,1,0, - 0,0,366,364,1,0,0,0,367,372,3,50,25,0,368,369,5,38,0,0,369,371,3,50,25, - 0,370,368,1,0,0,0,371,374,1,0,0,0,372,370,1,0,0,0,372,373,1,0,0,0,373,53, - 1,0,0,0,374,372,1,0,0,0,375,376,7,2,0,0,376,55,1,0,0,0,377,378,5,80,0,0, - 378,57,1,0,0,0,379,422,5,49,0,0,380,381,3,92,46,0,381,382,5,71,0,0,382, - 422,1,0,0,0,383,422,3,90,45,0,384,422,3,92,46,0,385,422,3,86,43,0,386,422, - 3,60,30,0,387,422,3,94,47,0,388,389,5,69,0,0,389,394,3,88,44,0,390,391, - 5,38,0,0,391,393,3,88,44,0,392,390,1,0,0,0,393,396,1,0,0,0,394,392,1,0, - 0,0,394,395,1,0,0,0,395,397,1,0,0,0,396,394,1,0,0,0,397,398,5,70,0,0,398, - 422,1,0,0,0,399,400,5,69,0,0,400,405,3,86,43,0,401,402,5,38,0,0,402,404, - 3,86,43,0,403,401,1,0,0,0,404,407,1,0,0,0,405,403,1,0,0,0,405,406,1,0,0, - 0,406,408,1,0,0,0,407,405,1,0,0,0,408,409,5,70,0,0,409,422,1,0,0,0,410, - 411,5,69,0,0,411,416,3,94,47,0,412,413,5,38,0,0,413,415,3,94,47,0,414,412, - 1,0,0,0,415,418,1,0,0,0,416,414,1,0,0,0,416,417,1,0,0,0,417,419,1,0,0,0, - 418,416,1,0,0,0,419,420,5,70,0,0,420,422,1,0,0,0,421,379,1,0,0,0,421,380, - 1,0,0,0,421,383,1,0,0,0,421,384,1,0,0,0,421,385,1,0,0,0,421,386,1,0,0,0, - 421,387,1,0,0,0,421,388,1,0,0,0,421,399,1,0,0,0,421,410,1,0,0,0,422,59, - 1,0,0,0,423,426,5,52,0,0,424,426,5,68,0,0,425,423,1,0,0,0,425,424,1,0,0, - 0,426,61,1,0,0,0,427,428,5,10,0,0,428,429,5,31,0,0,429,63,1,0,0,0,430,431, - 5,18,0,0,431,436,3,66,33,0,432,433,5,38,0,0,433,435,3,66,33,0,434,432,1, - 0,0,0,435,438,1,0,0,0,436,434,1,0,0,0,436,437,1,0,0,0,437,65,1,0,0,0,438, - 436,1,0,0,0,439,441,3,10,5,0,440,442,7,3,0,0,441,440,1,0,0,0,441,442,1, - 0,0,0,442,445,1,0,0,0,443,444,5,50,0,0,444,446,7,4,0,0,445,443,1,0,0,0, - 445,446,1,0,0,0,446,67,1,0,0,0,447,448,5,9,0,0,448,449,3,52,26,0,449,69, - 1,0,0,0,450,451,5,2,0,0,451,452,3,52,26,0,452,71,1,0,0,0,453,454,5,15,0, - 0,454,459,3,74,37,0,455,456,5,38,0,0,456,458,3,74,37,0,457,455,1,0,0,0, - 458,461,1,0,0,0,459,457,1,0,0,0,459,460,1,0,0,0,460,73,1,0,0,0,461,459, - 1,0,0,0,462,463,3,50,25,0,463,464,5,84,0,0,464,465,3,50,25,0,465,75,1,0, - 0,0,466,467,5,1,0,0,467,468,3,18,9,0,468,470,3,94,47,0,469,471,3,82,41, - 0,470,469,1,0,0,0,470,471,1,0,0,0,471,77,1,0,0,0,472,473,5,7,0,0,473,474, - 3,18,9,0,474,475,3,94,47,0,475,79,1,0,0,0,476,477,5,14,0,0,477,478,3,48, - 24,0,478,81,1,0,0,0,479,484,3,84,42,0,480,481,5,38,0,0,481,483,3,84,42, - 0,482,480,1,0,0,0,483,486,1,0,0,0,484,482,1,0,0,0,484,485,1,0,0,0,485,83, - 1,0,0,0,486,484,1,0,0,0,487,488,3,54,27,0,488,489,5,36,0,0,489,490,3,58, - 29,0,490,85,1,0,0,0,491,492,7,5,0,0,492,87,1,0,0,0,493,496,3,90,45,0,494, - 496,3,92,46,0,495,493,1,0,0,0,495,494,1,0,0,0,496,89,1,0,0,0,497,499,7, - 0,0,0,498,497,1,0,0,0,498,499,1,0,0,0,499,500,1,0,0,0,500,501,5,32,0,0, - 501,91,1,0,0,0,502,504,7,0,0,0,503,502,1,0,0,0,503,504,1,0,0,0,504,505, - 1,0,0,0,505,506,5,31,0,0,506,93,1,0,0,0,507,508,5,30,0,0,508,95,1,0,0,0, - 509,510,7,6,0,0,510,97,1,0,0,0,511,512,5,5,0,0,512,513,3,100,50,0,513,99, - 1,0,0,0,514,515,5,69,0,0,515,516,3,2,1,0,516,517,5,70,0,0,517,101,1,0,0, - 0,518,519,5,17,0,0,519,520,5,106,0,0,520,103,1,0,0,0,521,522,5,12,0,0,522, - 523,5,110,0,0,523,105,1,0,0,0,524,525,5,3,0,0,525,528,5,90,0,0,526,527, - 5,88,0,0,527,529,3,50,25,0,528,526,1,0,0,0,528,529,1,0,0,0,529,539,1,0, - 0,0,530,531,5,89,0,0,531,536,3,108,54,0,532,533,5,38,0,0,533,535,3,108, - 54,0,534,532,1,0,0,0,535,538,1,0,0,0,536,534,1,0,0,0,536,537,1,0,0,0,537, - 540,1,0,0,0,538,536,1,0,0,0,539,530,1,0,0,0,539,540,1,0,0,0,540,107,1,0, - 0,0,541,542,3,50,25,0,542,543,5,36,0,0,543,545,1,0,0,0,544,541,1,0,0,0, - 544,545,1,0,0,0,545,546,1,0,0,0,546,547,3,50,25,0,547,109,1,0,0,0,548,549, - 5,11,0,0,549,550,5,25,0,0,550,551,5,88,0,0,551,552,3,52,26,0,552,111,1, - 0,0,0,53,123,132,148,160,169,177,181,189,191,196,203,208,215,221,229,231, - 242,249,260,263,277,285,293,297,303,311,324,328,332,339,343,349,356,364, - 372,394,405,416,421,425,436,441,445,459,470,484,495,498,503,528,536,539, - 544]; + 7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,1,0,1,0,1,0,1,1,1,1,1,1,1, + 1,1,1,1,1,5,1,126,8,1,10,1,12,1,129,9,1,1,2,1,2,1,2,1,2,1,2,1,2,3,2,137, + 8,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,3,3,153,8,3, + 1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,165,8,5,1,5,1,5,1,5,1,5,1,5, + 5,5,172,8,5,10,5,12,5,175,9,5,1,5,1,5,1,5,1,5,1,5,3,5,182,8,5,1,5,1,5,3, + 5,186,8,5,1,5,1,5,1,5,1,5,1,5,1,5,5,5,194,8,5,10,5,12,5,197,9,5,1,6,1,6, + 3,6,201,8,6,1,6,1,6,1,6,1,6,1,6,3,6,208,8,6,1,6,1,6,1,6,3,6,213,8,6,1,7, + 1,7,1,7,1,7,1,7,3,7,220,8,7,1,8,1,8,1,8,1,8,3,8,226,8,8,1,8,1,8,1,8,1,8, + 1,8,1,8,5,8,234,8,8,10,8,12,8,237,9,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,3, + 9,247,8,9,1,9,1,9,1,9,5,9,252,8,9,10,9,12,9,255,9,9,1,10,1,10,1,10,1,10, + 1,10,1,10,5,10,263,8,10,10,10,12,10,266,9,10,3,10,268,8,10,1,10,1,10,1, + 11,1,11,1,12,1,12,1,12,1,13,1,13,1,13,5,13,280,8,13,10,13,12,13,283,9,13, + 1,14,1,14,1,14,1,14,1,14,3,14,290,8,14,1,15,1,15,1,15,1,15,5,15,296,8,15, + 10,15,12,15,299,9,15,1,15,3,15,302,8,15,1,16,1,16,1,16,1,16,1,16,3,16,309, + 8,16,1,17,1,17,1,18,1,18,1,19,1,19,3,19,317,8,19,1,20,1,20,1,20,1,20,5, + 20,323,8,20,10,20,12,20,326,9,20,1,21,1,21,1,21,1,21,1,22,1,22,1,22,1,22, + 5,22,336,8,22,10,22,12,22,339,9,22,1,22,3,22,342,8,22,1,22,1,22,3,22,346, + 8,22,1,23,1,23,1,23,1,24,1,24,3,24,353,8,24,1,24,1,24,3,24,357,8,24,1,25, + 1,25,1,25,1,25,3,25,363,8,25,1,26,1,26,1,26,5,26,368,8,26,10,26,12,26,371, + 9,26,1,27,1,27,1,27,5,27,376,8,27,10,27,12,27,379,9,27,1,28,1,28,1,28,5, + 28,384,8,28,10,28,12,28,387,9,28,1,29,1,29,1,30,1,30,1,31,1,31,1,31,1,31, + 1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,5,31,406,8,31,10,31,12,31, + 409,9,31,1,31,1,31,1,31,1,31,1,31,1,31,5,31,417,8,31,10,31,12,31,420,9, + 31,1,31,1,31,1,31,1,31,1,31,1,31,5,31,428,8,31,10,31,12,31,431,9,31,1,31, + 1,31,3,31,435,8,31,1,32,1,32,3,32,439,8,32,1,33,1,33,1,33,1,34,1,34,1,34, + 1,34,5,34,448,8,34,10,34,12,34,451,9,34,1,35,1,35,3,35,455,8,35,1,35,1, + 35,3,35,459,8,35,1,36,1,36,1,36,1,37,1,37,1,37,1,38,1,38,1,38,1,38,5,38, + 471,8,38,10,38,12,38,474,9,38,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,3, + 40,484,8,40,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,43,1,43,1,43,5,43,496, + 8,43,10,43,12,43,499,9,43,1,44,1,44,1,44,1,44,1,45,1,45,1,46,1,46,3,46, + 509,8,46,1,47,3,47,512,8,47,1,47,1,47,1,48,3,48,517,8,48,1,48,1,48,1,49, + 1,49,1,50,1,50,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,53,1,53,1,53,1,54,1, + 54,1,54,1,55,1,55,1,55,1,55,3,55,542,8,55,1,55,1,55,1,55,1,55,5,55,548, + 8,55,10,55,12,55,551,9,55,3,55,553,8,55,1,56,1,56,1,56,3,56,558,8,56,1, + 56,1,56,1,57,1,57,1,57,1,57,1,57,1,57,0,4,2,10,16,18,58,0,2,4,6,8,10,12, + 14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60, + 62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106, + 108,110,112,114,0,8,1,0,63,64,1,0,65,67,2,0,25,25,30,30,1,0,71,72,2,0,35, + 35,39,39,1,0,42,43,2,0,41,41,55,55,2,0,56,56,58,62,592,0,116,1,0,0,0,2, + 119,1,0,0,0,4,136,1,0,0,0,6,152,1,0,0,0,8,154,1,0,0,0,10,185,1,0,0,0,12, + 212,1,0,0,0,14,219,1,0,0,0,16,225,1,0,0,0,18,246,1,0,0,0,20,256,1,0,0,0, + 22,271,1,0,0,0,24,273,1,0,0,0,26,276,1,0,0,0,28,289,1,0,0,0,30,291,1,0, + 0,0,32,308,1,0,0,0,34,310,1,0,0,0,36,312,1,0,0,0,38,316,1,0,0,0,40,318, + 1,0,0,0,42,327,1,0,0,0,44,331,1,0,0,0,46,347,1,0,0,0,48,350,1,0,0,0,50, + 358,1,0,0,0,52,364,1,0,0,0,54,372,1,0,0,0,56,380,1,0,0,0,58,388,1,0,0,0, + 60,390,1,0,0,0,62,434,1,0,0,0,64,438,1,0,0,0,66,440,1,0,0,0,68,443,1,0, + 0,0,70,452,1,0,0,0,72,460,1,0,0,0,74,463,1,0,0,0,76,466,1,0,0,0,78,475, + 1,0,0,0,80,479,1,0,0,0,82,485,1,0,0,0,84,489,1,0,0,0,86,492,1,0,0,0,88, + 500,1,0,0,0,90,504,1,0,0,0,92,508,1,0,0,0,94,511,1,0,0,0,96,516,1,0,0,0, + 98,520,1,0,0,0,100,522,1,0,0,0,102,524,1,0,0,0,104,527,1,0,0,0,106,531, + 1,0,0,0,108,534,1,0,0,0,110,537,1,0,0,0,112,557,1,0,0,0,114,561,1,0,0,0, + 116,117,3,2,1,0,117,118,5,0,0,1,118,1,1,0,0,0,119,120,6,1,-1,0,120,121, + 3,4,2,0,121,127,1,0,0,0,122,123,10,1,0,0,123,124,5,29,0,0,124,126,3,6,3, + 0,125,122,1,0,0,0,126,129,1,0,0,0,127,125,1,0,0,0,127,128,1,0,0,0,128,3, + 1,0,0,0,129,127,1,0,0,0,130,137,3,102,51,0,131,137,3,30,15,0,132,137,3, + 24,12,0,133,137,3,44,22,0,134,137,3,106,53,0,135,137,3,108,54,0,136,130, + 1,0,0,0,136,131,1,0,0,0,136,132,1,0,0,0,136,133,1,0,0,0,136,134,1,0,0,0, + 136,135,1,0,0,0,137,5,1,0,0,0,138,153,3,46,23,0,139,153,3,50,25,0,140,153, + 3,66,33,0,141,153,3,114,57,0,142,153,3,72,36,0,143,153,3,68,34,0,144,153, + 3,48,24,0,145,153,3,8,4,0,146,153,3,74,37,0,147,153,3,76,38,0,148,153,3, + 80,40,0,149,153,3,82,41,0,150,153,3,110,55,0,151,153,3,84,42,0,152,138, + 1,0,0,0,152,139,1,0,0,0,152,140,1,0,0,0,152,141,1,0,0,0,152,142,1,0,0,0, + 152,143,1,0,0,0,152,144,1,0,0,0,152,145,1,0,0,0,152,146,1,0,0,0,152,147, + 1,0,0,0,152,148,1,0,0,0,152,149,1,0,0,0,152,150,1,0,0,0,152,151,1,0,0,0, + 153,7,1,0,0,0,154,155,5,20,0,0,155,156,3,10,5,0,156,9,1,0,0,0,157,158,6, + 5,-1,0,158,159,5,48,0,0,159,186,3,10,5,7,160,186,3,14,7,0,161,186,3,12, + 6,0,162,164,3,14,7,0,163,165,5,48,0,0,164,163,1,0,0,0,164,165,1,0,0,0,165, + 166,1,0,0,0,166,167,5,45,0,0,167,168,5,44,0,0,168,173,3,14,7,0,169,170, + 5,38,0,0,170,172,3,14,7,0,171,169,1,0,0,0,172,175,1,0,0,0,173,171,1,0,0, + 0,173,174,1,0,0,0,174,176,1,0,0,0,175,173,1,0,0,0,176,177,5,54,0,0,177, + 186,1,0,0,0,178,179,3,14,7,0,179,181,5,46,0,0,180,182,5,48,0,0,181,180, + 1,0,0,0,181,182,1,0,0,0,182,183,1,0,0,0,183,184,5,49,0,0,184,186,1,0,0, + 0,185,157,1,0,0,0,185,160,1,0,0,0,185,161,1,0,0,0,185,162,1,0,0,0,185,178, + 1,0,0,0,186,195,1,0,0,0,187,188,10,4,0,0,188,189,5,34,0,0,189,194,3,10, + 5,5,190,191,10,3,0,0,191,192,5,51,0,0,192,194,3,10,5,4,193,187,1,0,0,0, + 193,190,1,0,0,0,194,197,1,0,0,0,195,193,1,0,0,0,195,196,1,0,0,0,196,11, + 1,0,0,0,197,195,1,0,0,0,198,200,3,14,7,0,199,201,5,48,0,0,200,199,1,0,0, + 0,200,201,1,0,0,0,201,202,1,0,0,0,202,203,5,47,0,0,203,204,3,98,49,0,204, + 213,1,0,0,0,205,207,3,14,7,0,206,208,5,48,0,0,207,206,1,0,0,0,207,208,1, + 0,0,0,208,209,1,0,0,0,209,210,5,53,0,0,210,211,3,98,49,0,211,213,1,0,0, + 0,212,198,1,0,0,0,212,205,1,0,0,0,213,13,1,0,0,0,214,220,3,16,8,0,215,216, + 3,16,8,0,216,217,3,100,50,0,217,218,3,16,8,0,218,220,1,0,0,0,219,214,1, + 0,0,0,219,215,1,0,0,0,220,15,1,0,0,0,221,222,6,8,-1,0,222,226,3,18,9,0, + 223,224,7,0,0,0,224,226,3,16,8,3,225,221,1,0,0,0,225,223,1,0,0,0,226,235, + 1,0,0,0,227,228,10,2,0,0,228,229,7,1,0,0,229,234,3,16,8,3,230,231,10,1, + 0,0,231,232,7,0,0,0,232,234,3,16,8,2,233,227,1,0,0,0,233,230,1,0,0,0,234, + 237,1,0,0,0,235,233,1,0,0,0,235,236,1,0,0,0,236,17,1,0,0,0,237,235,1,0, + 0,0,238,239,6,9,-1,0,239,247,3,62,31,0,240,247,3,52,26,0,241,247,3,20,10, + 0,242,243,5,44,0,0,243,244,3,10,5,0,244,245,5,54,0,0,245,247,1,0,0,0,246, + 238,1,0,0,0,246,240,1,0,0,0,246,241,1,0,0,0,246,242,1,0,0,0,247,253,1,0, + 0,0,248,249,10,1,0,0,249,250,5,37,0,0,250,252,3,22,11,0,251,248,1,0,0,0, + 252,255,1,0,0,0,253,251,1,0,0,0,253,254,1,0,0,0,254,19,1,0,0,0,255,253, + 1,0,0,0,256,257,3,58,29,0,257,267,5,44,0,0,258,268,5,65,0,0,259,264,3,10, + 5,0,260,261,5,38,0,0,261,263,3,10,5,0,262,260,1,0,0,0,263,266,1,0,0,0,264, + 262,1,0,0,0,264,265,1,0,0,0,265,268,1,0,0,0,266,264,1,0,0,0,267,258,1,0, + 0,0,267,259,1,0,0,0,267,268,1,0,0,0,268,269,1,0,0,0,269,270,5,54,0,0,270, + 21,1,0,0,0,271,272,3,58,29,0,272,23,1,0,0,0,273,274,5,16,0,0,274,275,3, + 26,13,0,275,25,1,0,0,0,276,281,3,28,14,0,277,278,5,38,0,0,278,280,3,28, + 14,0,279,277,1,0,0,0,280,283,1,0,0,0,281,279,1,0,0,0,281,282,1,0,0,0,282, + 27,1,0,0,0,283,281,1,0,0,0,284,290,3,10,5,0,285,286,3,52,26,0,286,287,5, + 36,0,0,287,288,3,10,5,0,288,290,1,0,0,0,289,284,1,0,0,0,289,285,1,0,0,0, + 290,29,1,0,0,0,291,292,5,6,0,0,292,297,3,32,16,0,293,294,5,38,0,0,294,296, + 3,32,16,0,295,293,1,0,0,0,296,299,1,0,0,0,297,295,1,0,0,0,297,298,1,0,0, + 0,298,301,1,0,0,0,299,297,1,0,0,0,300,302,3,38,19,0,301,300,1,0,0,0,301, + 302,1,0,0,0,302,31,1,0,0,0,303,304,3,34,17,0,304,305,5,114,0,0,305,306, + 3,36,18,0,306,309,1,0,0,0,307,309,3,36,18,0,308,303,1,0,0,0,308,307,1,0, + 0,0,309,33,1,0,0,0,310,311,5,25,0,0,311,35,1,0,0,0,312,313,7,2,0,0,313, + 37,1,0,0,0,314,317,3,40,20,0,315,317,3,42,21,0,316,314,1,0,0,0,316,315, + 1,0,0,0,317,39,1,0,0,0,318,319,5,76,0,0,319,324,5,25,0,0,320,321,5,38,0, + 0,321,323,5,25,0,0,322,320,1,0,0,0,323,326,1,0,0,0,324,322,1,0,0,0,324, + 325,1,0,0,0,325,41,1,0,0,0,326,324,1,0,0,0,327,328,5,69,0,0,328,329,3,40, + 20,0,329,330,5,70,0,0,330,43,1,0,0,0,331,332,5,13,0,0,332,337,3,32,16,0, + 333,334,5,38,0,0,334,336,3,32,16,0,335,333,1,0,0,0,336,339,1,0,0,0,337, + 335,1,0,0,0,337,338,1,0,0,0,338,341,1,0,0,0,339,337,1,0,0,0,340,342,3,26, + 13,0,341,340,1,0,0,0,341,342,1,0,0,0,342,345,1,0,0,0,343,344,5,33,0,0,344, + 346,3,26,13,0,345,343,1,0,0,0,345,346,1,0,0,0,346,45,1,0,0,0,347,348,5, + 4,0,0,348,349,3,26,13,0,349,47,1,0,0,0,350,352,5,19,0,0,351,353,3,26,13, + 0,352,351,1,0,0,0,352,353,1,0,0,0,353,356,1,0,0,0,354,355,5,33,0,0,355, + 357,3,26,13,0,356,354,1,0,0,0,356,357,1,0,0,0,357,49,1,0,0,0,358,359,5, + 8,0,0,359,362,3,26,13,0,360,361,5,33,0,0,361,363,3,26,13,0,362,360,1,0, + 0,0,362,363,1,0,0,0,363,51,1,0,0,0,364,369,3,58,29,0,365,366,5,40,0,0,366, + 368,3,58,29,0,367,365,1,0,0,0,368,371,1,0,0,0,369,367,1,0,0,0,369,370,1, + 0,0,0,370,53,1,0,0,0,371,369,1,0,0,0,372,377,3,60,30,0,373,374,5,40,0,0, + 374,376,3,60,30,0,375,373,1,0,0,0,376,379,1,0,0,0,377,375,1,0,0,0,377,378, + 1,0,0,0,378,55,1,0,0,0,379,377,1,0,0,0,380,385,3,54,27,0,381,382,5,38,0, + 0,382,384,3,54,27,0,383,381,1,0,0,0,384,387,1,0,0,0,385,383,1,0,0,0,385, + 386,1,0,0,0,386,57,1,0,0,0,387,385,1,0,0,0,388,389,7,3,0,0,389,59,1,0,0, + 0,390,391,5,80,0,0,391,61,1,0,0,0,392,435,5,49,0,0,393,394,3,96,48,0,394, + 395,5,71,0,0,395,435,1,0,0,0,396,435,3,94,47,0,397,435,3,96,48,0,398,435, + 3,90,45,0,399,435,3,64,32,0,400,435,3,98,49,0,401,402,5,69,0,0,402,407, + 3,92,46,0,403,404,5,38,0,0,404,406,3,92,46,0,405,403,1,0,0,0,406,409,1, + 0,0,0,407,405,1,0,0,0,407,408,1,0,0,0,408,410,1,0,0,0,409,407,1,0,0,0,410, + 411,5,70,0,0,411,435,1,0,0,0,412,413,5,69,0,0,413,418,3,90,45,0,414,415, + 5,38,0,0,415,417,3,90,45,0,416,414,1,0,0,0,417,420,1,0,0,0,418,416,1,0, + 0,0,418,419,1,0,0,0,419,421,1,0,0,0,420,418,1,0,0,0,421,422,5,70,0,0,422, + 435,1,0,0,0,423,424,5,69,0,0,424,429,3,98,49,0,425,426,5,38,0,0,426,428, + 3,98,49,0,427,425,1,0,0,0,428,431,1,0,0,0,429,427,1,0,0,0,429,430,1,0,0, + 0,430,432,1,0,0,0,431,429,1,0,0,0,432,433,5,70,0,0,433,435,1,0,0,0,434, + 392,1,0,0,0,434,393,1,0,0,0,434,396,1,0,0,0,434,397,1,0,0,0,434,398,1,0, + 0,0,434,399,1,0,0,0,434,400,1,0,0,0,434,401,1,0,0,0,434,412,1,0,0,0,434, + 423,1,0,0,0,435,63,1,0,0,0,436,439,5,52,0,0,437,439,5,68,0,0,438,436,1, + 0,0,0,438,437,1,0,0,0,439,65,1,0,0,0,440,441,5,10,0,0,441,442,5,31,0,0, + 442,67,1,0,0,0,443,444,5,18,0,0,444,449,3,70,35,0,445,446,5,38,0,0,446, + 448,3,70,35,0,447,445,1,0,0,0,448,451,1,0,0,0,449,447,1,0,0,0,449,450,1, + 0,0,0,450,69,1,0,0,0,451,449,1,0,0,0,452,454,3,10,5,0,453,455,7,4,0,0,454, + 453,1,0,0,0,454,455,1,0,0,0,455,458,1,0,0,0,456,457,5,50,0,0,457,459,7, + 5,0,0,458,456,1,0,0,0,458,459,1,0,0,0,459,71,1,0,0,0,460,461,5,9,0,0,461, + 462,3,56,28,0,462,73,1,0,0,0,463,464,5,2,0,0,464,465,3,56,28,0,465,75,1, + 0,0,0,466,467,5,15,0,0,467,472,3,78,39,0,468,469,5,38,0,0,469,471,3,78, + 39,0,470,468,1,0,0,0,471,474,1,0,0,0,472,470,1,0,0,0,472,473,1,0,0,0,473, + 77,1,0,0,0,474,472,1,0,0,0,475,476,3,54,27,0,476,477,5,84,0,0,477,478,3, + 54,27,0,478,79,1,0,0,0,479,480,5,1,0,0,480,481,3,18,9,0,481,483,3,98,49, + 0,482,484,3,86,43,0,483,482,1,0,0,0,483,484,1,0,0,0,484,81,1,0,0,0,485, + 486,5,7,0,0,486,487,3,18,9,0,487,488,3,98,49,0,488,83,1,0,0,0,489,490,5, + 14,0,0,490,491,3,52,26,0,491,85,1,0,0,0,492,497,3,88,44,0,493,494,5,38, + 0,0,494,496,3,88,44,0,495,493,1,0,0,0,496,499,1,0,0,0,497,495,1,0,0,0,497, + 498,1,0,0,0,498,87,1,0,0,0,499,497,1,0,0,0,500,501,3,58,29,0,501,502,5, + 36,0,0,502,503,3,62,31,0,503,89,1,0,0,0,504,505,7,6,0,0,505,91,1,0,0,0, + 506,509,3,94,47,0,507,509,3,96,48,0,508,506,1,0,0,0,508,507,1,0,0,0,509, + 93,1,0,0,0,510,512,7,0,0,0,511,510,1,0,0,0,511,512,1,0,0,0,512,513,1,0, + 0,0,513,514,5,32,0,0,514,95,1,0,0,0,515,517,7,0,0,0,516,515,1,0,0,0,516, + 517,1,0,0,0,517,518,1,0,0,0,518,519,5,31,0,0,519,97,1,0,0,0,520,521,5,30, + 0,0,521,99,1,0,0,0,522,523,7,7,0,0,523,101,1,0,0,0,524,525,5,5,0,0,525, + 526,3,104,52,0,526,103,1,0,0,0,527,528,5,69,0,0,528,529,3,2,1,0,529,530, + 5,70,0,0,530,105,1,0,0,0,531,532,5,17,0,0,532,533,5,106,0,0,533,107,1,0, + 0,0,534,535,5,12,0,0,535,536,5,110,0,0,536,109,1,0,0,0,537,538,5,3,0,0, + 538,541,5,90,0,0,539,540,5,88,0,0,540,542,3,54,27,0,541,539,1,0,0,0,541, + 542,1,0,0,0,542,552,1,0,0,0,543,544,5,89,0,0,544,549,3,112,56,0,545,546, + 5,38,0,0,546,548,3,112,56,0,547,545,1,0,0,0,548,551,1,0,0,0,549,547,1,0, + 0,0,549,550,1,0,0,0,550,553,1,0,0,0,551,549,1,0,0,0,552,543,1,0,0,0,552, + 553,1,0,0,0,553,111,1,0,0,0,554,555,3,54,27,0,555,556,5,36,0,0,556,558, + 1,0,0,0,557,554,1,0,0,0,557,558,1,0,0,0,558,559,1,0,0,0,559,560,3,54,27, + 0,560,113,1,0,0,0,561,562,5,11,0,0,562,563,3,32,16,0,563,564,5,88,0,0,564, + 565,3,56,28,0,565,115,1,0,0,0,54,127,136,152,164,173,181,185,193,195,200, + 207,212,219,225,233,235,246,253,264,267,281,289,297,301,308,316,324,337, + 341,345,352,356,362,369,377,385,407,418,429,434,438,449,454,458,472,483, + 497,508,511,516,541,549,552,557]; private static __ATN: ATN; public static get _ATN(): ATN { @@ -4262,11 +4344,11 @@ export class FromCommandContext extends ParserRuleContext { public FROM(): TerminalNode { return this.getToken(esql_parser.FROM, 0); } - public indexIdentifier_list(): IndexIdentifierContext[] { - return this.getTypedRuleContexts(IndexIdentifierContext) as IndexIdentifierContext[]; + public indexPattern_list(): IndexPatternContext[] { + return this.getTypedRuleContexts(IndexPatternContext) as IndexPatternContext[]; } - public indexIdentifier(i: number): IndexIdentifierContext { - return this.getTypedRuleContext(IndexIdentifierContext, i) as IndexIdentifierContext; + public indexPattern(i: number): IndexPatternContext { + return this.getTypedRuleContext(IndexPatternContext, i) as IndexPatternContext; } public COMMA_list(): TerminalNode[] { return this.getTokens(esql_parser.COMMA); @@ -4293,25 +4375,82 @@ export class FromCommandContext extends ParserRuleContext { } -export class IndexIdentifierContext extends ParserRuleContext { +export class IndexPatternContext extends ParserRuleContext { constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); this.parser = parser; } - public INDEX_UNQUOTED_IDENTIFIER(): TerminalNode { - return this.getToken(esql_parser.INDEX_UNQUOTED_IDENTIFIER, 0); + public clusterString(): ClusterStringContext { + return this.getTypedRuleContext(ClusterStringContext, 0) as ClusterStringContext; + } + public COLON(): TerminalNode { + return this.getToken(esql_parser.COLON, 0); + } + public indexString(): IndexStringContext { + return this.getTypedRuleContext(IndexStringContext, 0) as IndexStringContext; } public get ruleIndex(): number { - return esql_parser.RULE_indexIdentifier; + return esql_parser.RULE_indexPattern; } public enterRule(listener: esql_parserListener): void { - if(listener.enterIndexIdentifier) { - listener.enterIndexIdentifier(this); + if(listener.enterIndexPattern) { + listener.enterIndexPattern(this); } } public exitRule(listener: esql_parserListener): void { - if(listener.exitIndexIdentifier) { - listener.exitIndexIdentifier(this); + if(listener.exitIndexPattern) { + listener.exitIndexPattern(this); + } + } +} + + +export class ClusterStringContext extends ParserRuleContext { + constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public UNQUOTED_SOURCE(): TerminalNode { + return this.getToken(esql_parser.UNQUOTED_SOURCE, 0); + } + public get ruleIndex(): number { + return esql_parser.RULE_clusterString; + } + public enterRule(listener: esql_parserListener): void { + if(listener.enterClusterString) { + listener.enterClusterString(this); + } + } + public exitRule(listener: esql_parserListener): void { + if(listener.exitClusterString) { + listener.exitClusterString(this); + } + } +} + + +export class IndexStringContext extends ParserRuleContext { + constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public UNQUOTED_SOURCE(): TerminalNode { + return this.getToken(esql_parser.UNQUOTED_SOURCE, 0); + } + public QUOTED_STRING(): TerminalNode { + return this.getToken(esql_parser.QUOTED_STRING, 0); + } + public get ruleIndex(): number { + return esql_parser.RULE_indexString; + } + public enterRule(listener: esql_parserListener): void { + if(listener.enterIndexString) { + listener.enterIndexString(this); + } + } + public exitRule(listener: esql_parserListener): void { + if(listener.exitIndexString) { + listener.exitIndexString(this); } } } @@ -4352,11 +4491,11 @@ export class MetadataOptionContext extends ParserRuleContext { public METADATA(): TerminalNode { return this.getToken(esql_parser.METADATA, 0); } - public indexIdentifier_list(): IndexIdentifierContext[] { - return this.getTypedRuleContexts(IndexIdentifierContext) as IndexIdentifierContext[]; + public UNQUOTED_SOURCE_list(): TerminalNode[] { + return this.getTokens(esql_parser.UNQUOTED_SOURCE); } - public indexIdentifier(i: number): IndexIdentifierContext { - return this.getTypedRuleContext(IndexIdentifierContext, i) as IndexIdentifierContext; + public UNQUOTED_SOURCE(i: number): TerminalNode { + return this.getToken(esql_parser.UNQUOTED_SOURCE, i); } public COMMA_list(): TerminalNode[] { return this.getTokens(esql_parser.COMMA); @@ -4364,7 +4503,7 @@ export class MetadataOptionContext extends ParserRuleContext { public COMMA(i: number): TerminalNode { return this.getToken(esql_parser.COMMA, i); } - public get ruleIndex(): number { + public get ruleIndex(): number { return esql_parser.RULE_metadataOption; } public enterRule(listener: esql_parserListener): void { @@ -4420,11 +4559,11 @@ export class MetricsCommandContext extends ParserRuleContext { public METRICS(): TerminalNode { return this.getToken(esql_parser.METRICS, 0); } - public indexIdentifier_list(): IndexIdentifierContext[] { - return this.getTypedRuleContexts(IndexIdentifierContext) as IndexIdentifierContext[]; + public indexPattern_list(): IndexPatternContext[] { + return this.getTypedRuleContexts(IndexPatternContext) as IndexPatternContext[]; } - public indexIdentifier(i: number): IndexIdentifierContext { - return this.getTypedRuleContext(IndexIdentifierContext, i) as IndexIdentifierContext; + public indexPattern(i: number): IndexPatternContext { + return this.getTypedRuleContext(IndexPatternContext, i) as IndexPatternContext; } public COMMA_list(): TerminalNode[] { return this.getTokens(esql_parser.COMMA); @@ -5776,7 +5915,7 @@ export class EnrichWithClauseContext extends ParserRuleContext { export class LookupCommandContext extends ParserRuleContext { - public _tableName!: Token; + public _tableName!: IndexPatternContext; public _matchFields!: QualifiedNamePatternsContext; constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -5788,8 +5927,8 @@ export class LookupCommandContext extends ParserRuleContext { public ON(): TerminalNode { return this.getToken(esql_parser.ON, 0); } - public INDEX_UNQUOTED_IDENTIFIER(): TerminalNode { - return this.getToken(esql_parser.INDEX_UNQUOTED_IDENTIFIER, 0); + public indexPattern(): IndexPatternContext { + return this.getTypedRuleContext(IndexPatternContext, 0) as IndexPatternContext; } public qualifiedNamePatterns(): QualifiedNamePatternsContext { return this.getTypedRuleContext(QualifiedNamePatternsContext, 0) as QualifiedNamePatternsContext; diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts b/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts index eee7a421a1593..e6a28c8d71828 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts @@ -33,7 +33,9 @@ import { RowCommandContext } from "./esql_parser"; import { FieldsContext } from "./esql_parser"; import { FieldContext } from "./esql_parser"; import { FromCommandContext } from "./esql_parser"; -import { IndexIdentifierContext } from "./esql_parser"; +import { IndexPatternContext } from "./esql_parser"; +import { ClusterStringContext } from "./esql_parser"; +import { IndexStringContext } from "./esql_parser"; import { MetadataContext } from "./esql_parser"; import { MetadataOptionContext } from "./esql_parser"; import { Deprecated_metadataContext } from "./esql_parser"; @@ -419,15 +421,35 @@ export default class esql_parserListener extends ParseTreeListener { */ exitFromCommand?: (ctx: FromCommandContext) => void; /** - * Enter a parse tree produced by `esql_parser.indexIdentifier`. + * Enter a parse tree produced by `esql_parser.indexPattern`. * @param ctx the parse tree */ - enterIndexIdentifier?: (ctx: IndexIdentifierContext) => void; + enterIndexPattern?: (ctx: IndexPatternContext) => void; /** - * Exit a parse tree produced by `esql_parser.indexIdentifier`. + * Exit a parse tree produced by `esql_parser.indexPattern`. * @param ctx the parse tree */ - exitIndexIdentifier?: (ctx: IndexIdentifierContext) => void; + exitIndexPattern?: (ctx: IndexPatternContext) => void; + /** + * Enter a parse tree produced by `esql_parser.clusterString`. + * @param ctx the parse tree + */ + enterClusterString?: (ctx: ClusterStringContext) => void; + /** + * Exit a parse tree produced by `esql_parser.clusterString`. + * @param ctx the parse tree + */ + exitClusterString?: (ctx: ClusterStringContext) => void; + /** + * Enter a parse tree produced by `esql_parser.indexString`. + * @param ctx the parse tree + */ + enterIndexString?: (ctx: IndexStringContext) => void; + /** + * Exit a parse tree produced by `esql_parser.indexString`. + * @param ctx the parse tree + */ + exitIndexString?: (ctx: IndexStringContext) => void; /** * Enter a parse tree produced by `esql_parser.metadata`. * @param ctx the parse tree diff --git a/packages/kbn-esql-ast/src/ast_factory.ts b/packages/kbn-esql-ast/src/ast_factory.ts index 9d76ff3a7cd4d..18c2a3c7ecbec 100644 --- a/packages/kbn-esql-ast/src/ast_factory.ts +++ b/packages/kbn-esql-ast/src/ast_factory.ts @@ -29,7 +29,7 @@ import { default as esql_parser, type MetaCommandContext, type MetricsCommandContext, - IndexIdentifierContext, + IndexPatternContext, } from './antlr/esql_parser'; import { default as ESQLParserListener } from './antlr/esql_parser_listener'; import { @@ -154,7 +154,7 @@ export class AstListener implements ESQLParserListener { type: 'command', args: [], sources: ctx - .getTypedRuleContexts(IndexIdentifierContext) + .getTypedRuleContexts(IndexPatternContext) .map((sourceCtx) => createSource(sourceCtx)), }; this.ast.push(node); diff --git a/packages/kbn-esql-ast/src/ast_helpers.ts b/packages/kbn-esql-ast/src/ast_helpers.ts index 76e130f233807..f3c5ad0b0df18 100644 --- a/packages/kbn-esql-ast/src/ast_helpers.ts +++ b/packages/kbn-esql-ast/src/ast_helpers.ts @@ -275,12 +275,24 @@ function safeBackticksRemoval(text: string | undefined) { return text?.replace(TICKS_REGEX, '').replace(DOUBLE_TICKS_REGEX, SINGLE_BACKTICK) || ''; } +function sanitizeSourceString(ctx: ParserRuleContext) { + const contextText = ctx.getText(); + // If wrapped by triple quote, remove + if (contextText.startsWith(`"""`) && contextText.endsWith(`"""`)) { + return contextText.replace(/\"\"\"/g, ''); + } + // If wrapped by single quote, remove + if (contextText.startsWith(`"`) && contextText.endsWith(`"`)) { + return contextText.slice(1, -1); + } + return contextText; +} + export function sanitizeIdentifierString(ctx: ParserRuleContext) { const result = getUnquotedText(ctx)?.getText() || safeBackticksRemoval(getQuotedText(ctx)?.getText()) || safeBackticksRemoval(ctx.getText()); // for some reason some quoted text is not detected correctly by the parser - // TODO - understand why is now returned as the match text for the FROM command return result === '' ? '' : result; } @@ -321,7 +333,7 @@ export function createSource( ctx: ParserRuleContext, type: 'index' | 'policy' = 'index' ): ESQLSource { - const text = sanitizeIdentifierString(ctx); + const text = sanitizeSourceString(ctx); return { type: 'source', name: text, diff --git a/packages/kbn-esql-ast/src/ast_walker.ts b/packages/kbn-esql-ast/src/ast_walker.ts index 870301b938a65..33aa91f66a2a6 100644 --- a/packages/kbn-esql-ast/src/ast_walker.ts +++ b/packages/kbn-esql-ast/src/ast_walker.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { type ParserRuleContext } from 'antlr4'; +import { ParserRuleContext, TerminalNode } from 'antlr4'; import { default as esql_parser, ArithmeticBinaryContext, @@ -57,10 +57,10 @@ import { StringLiteralContext, type ValueExpressionContext, ValueExpressionDefaultContext, - IndexIdentifierContext, InlineCastContext, InputNamedOrPositionalParamContext, InputParamContext, + IndexPatternContext, } from './antlr/esql_parser'; import { createSource, @@ -98,16 +98,27 @@ import type { } from './types'; export function collectAllSourceIdentifiers(ctx: FromCommandContext): ESQLAstItem[] { - const fromContexts = ctx.getTypedRuleContexts(IndexIdentifierContext); - + const fromContexts = ctx.getTypedRuleContexts(IndexPatternContext); return fromContexts.map((sourceCtx) => createSource(sourceCtx)); } +function terminalNodeToParserRuleContext(node: TerminalNode): ParserRuleContext { + const context = new ParserRuleContext(); + context.start = node.symbol; + context.stop = node.symbol; + context.children = [node]; + return context; +} function extractIdentifiers( ctx: KeepCommandContext | DropCommandContext | MvExpandCommandContext | MetadataOptionContext ) { if (ctx instanceof MetadataOptionContext) { - return wrapIdentifierAsArray(ctx.indexIdentifier_list()); + return ctx + .UNQUOTED_SOURCE_list() + .map((node) => { + return terminalNodeToParserRuleContext(node); + }) + .flat(); } if (ctx instanceof MvExpandCommandContext) { return wrapIdentifierAsArray(ctx.qualifiedName()); diff --git a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts index b1bafc2ce4359..ab5c1ca04ac7f 100644 --- a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts +++ b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts @@ -64,6 +64,12 @@ describe('esql query helpers', () => { 'METRICS pods load=avg(cpu), writes=max(rate(indexing_requests)) BY pod | SORT pod' ); expect(idxPattern16).toBe('pods'); + + const idxPattern17 = getIndexPatternFromESQLQuery('FROM "$foo%"'); + expect(idxPattern17).toBe('$foo%'); + + const idxPattern18 = getIndexPatternFromESQLQuery('FROM """foo-{{mm-dd_yy}}"""'); + expect(idxPattern18).toBe('foo-{{mm-dd_yy}}'); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/BUILD.bazel b/packages/kbn-esql-validation-autocomplete/BUILD.bazel index 07106718615d1..1e956d1903dca 100644 --- a/packages/kbn-esql-validation-autocomplete/BUILD.bazel +++ b/packages/kbn-esql-validation-autocomplete/BUILD.bazel @@ -22,7 +22,7 @@ SRCS = glob( SHARED_DEPS = [ "//packages/kbn-i18n", - "@npm//js-levenshtein", + "@npm//fastest-levenshtein", ] js_library( diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts index d50d7cc903fce..aa92c7bd024d5 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts @@ -145,6 +145,39 @@ const dateDiffOptions = [ 'ns', ]; +const dateExtractOptions = [ + 'ALIGNED_DAY_OF_WEEK_IN_MONTH', + 'ALIGNED_DAY_OF_WEEK_IN_YEAR', + 'ALIGNED_WEEK_OF_MONTH', + 'ALIGNED_WEEK_OF_YEAR', + 'AMPM_OF_DAY', + 'CLOCK_HOUR_OF_AMPM', + 'CLOCK_HOUR_OF_DAY', + 'DAY_OF_MONTH', + 'DAY_OF_WEEK', + 'DAY_OF_YEAR', + 'EPOCH_DAY', + 'ERA', + 'HOUR_OF_AMPM', + 'HOUR_OF_DAY', + 'INSTANT_SECONDS', + 'MICRO_OF_DAY', + 'MICRO_OF_SECOND', + 'MILLI_OF_DAY', + 'MILLI_OF_SECOND', + 'MINUTE_OF_DAY', + 'MINUTE_OF_HOUR', + 'MONTH_OF_YEAR', + 'NANO_OF_DAY', + 'NANO_OF_SECOND', + 'OFFSET_SECONDS', + 'PROLEPTIC_MONTH', + 'SECOND_OF_DAY', + 'SECOND_OF_MINUTE', + 'YEAR', + 'YEAR_OF_ERA', +]; + /** * Enrichments for function definitions * @@ -168,8 +201,7 @@ const functionEnrichments: Record> date_extract: { signatures: [ { - // override the first param as type chrono_literal - params: [{ type: 'chrono_literal' }], + params: [{ literalOptions: dateExtractOptions }], }, ], }, diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts index 0ace35433b67e..8d6394fe96af6 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts @@ -15,7 +15,7 @@ import { statsAggregationFunctionDefinitions } from '../src/definitions/aggs'; import { evalFunctionDefinitions } from '../src/definitions/functions'; import { groupingFunctionDefinitions } from '../src/definitions/grouping'; import { getFunctionSignatures } from '../src/definitions/helpers'; -import { timeUnits, chronoLiterals } from '../src/definitions/literals'; +import { timeUnits } from '../src/definitions/literals'; import { nonNullable } from '../src/shared/helpers'; import { SupportedFieldType, @@ -1045,14 +1045,10 @@ function getFieldName( } const literals = { - chrono_literal: chronoLiterals[0].name, time_literal: timeUnits[0], }; -function getLiteralType(typeString: 'chrono_literal' | 'time_literal') { - if (typeString === 'chrono_literal') { - return literals[typeString]; - } +function getLiteralType(typeString: 'time_literal') { return `1 ${literals[typeString]}`; } @@ -1116,7 +1112,7 @@ function getFieldMapping( } if (/literal$/.test(typeString) && useLiterals) { return { - name: getLiteralType(typeString as 'chrono_literal' | 'time_literal'), + name: getLiteralType(typeString as 'time_literal'), type, ...rest, }; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts index 6f0562d7f118e..2f87d392a702e 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts @@ -10,7 +10,7 @@ import { suggest } from './autocomplete'; import { evalFunctionDefinitions } from '../definitions/functions'; import { builtinFunctions } from '../definitions/builtin'; import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; -import { chronoLiterals, timeUnitsToSuggest } from '../definitions/literals'; +import { timeUnitsToSuggest } from '../definitions/literals'; import { commandDefinitions } from '../definitions/commands'; import { getUnitDuration, TRIGGER_SUGGESTION_COMMAND } from './factories'; import { camelCase, partition } from 'lodash'; @@ -53,15 +53,27 @@ const fields: Array<{ name: string; type: string; suggestedAs?: string }> = [ ]; const indexes = ([] as Array<{ name: string; hidden: boolean; suggestedAs?: string }>).concat( - ['a', 'index', 'otherIndex', '.secretIndex', 'my-index'].map((name) => ({ + [ + 'a', + 'index', + 'otherIndex', + '.secretIndex', + 'my-index', + 'my-index$', + 'my_index{}', + 'my-index+1', + 'synthetics-*', + ].map((name) => ({ name, hidden: name.startsWith('.'), })), - ['my-index[quoted]', 'my-index$', 'my_index{}'].map((name) => ({ - name, - hidden: false, - suggestedAs: `\`${name}\``, - })) + ['my-index[quoted]', 'my:index', 'my,index', 'logstash-{now/d{yyyy.MM.dd|+12:00}}'].map( + (name) => ({ + name, + hidden: false, + suggestedAs: `"${name}"`, + }) + ) ); const integrations: Integration[] = ['nginx', 'k8s'].map((name) => ({ @@ -207,9 +219,6 @@ function getLiteralsByType(_type: string | string[]) { // return only singular return timeUnitsToSuggest.map(({ name }) => `1 ${name}`).filter((s) => !/s$/.test(s)); } - if (type.includes('chrono_literal')) { - return chronoLiterals.map(({ name }) => name); - } return []; } @@ -368,8 +377,9 @@ describe('autocomplete', () => { }); describe('from', () => { - const suggestedIndexes = indexes.filter(({ hidden }) => !hidden).map(({ name }) => name); - + const suggestedIndexes = indexes + .filter(({ hidden }) => !hidden) + .map(({ name, suggestedAs }) => suggestedAs || name); // Monaco will filter further down here testSuggestions( 'f', @@ -397,8 +407,7 @@ describe('autocomplete', () => { const dataSources = indexes.concat(integrations); const suggestedDataSources = dataSources .filter(({ hidden }) => !hidden) - .map(({ name }) => name); - + .map(({ name, suggestedAs }) => suggestedAs || name); testSuggestions('from ', suggestedDataSources, '', [undefined, dataSources, undefined]); testSuggestions('from a,', suggestedDataSources, '', [undefined, dataSources, undefined]); testSuggestions('from *,', suggestedDataSources, '', [undefined, dataSources, undefined]); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts index add47e4a5547b..03504552370b6 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts @@ -86,6 +86,7 @@ import { getQueryForFields, getSourcesFromCommands, isAggFunctionUsedAlready, + removeQuoteForSuggestedSources, } from './helper'; import { FunctionParameter } from '../definitions/types'; @@ -857,19 +858,28 @@ async function getExpressionSuggestionsByType( suggestions.push(...(policies.length ? policies : [buildNoPoliciesAvailableDefinition()])); } else { const index = getSourcesFromCommands(commands, 'index'); + const canRemoveQuote = isNewExpression && innerText.includes('"'); + // This is going to be empty for simple indices, and not empty for integrations if (index && index.text && index.text !== EDITOR_MARKER) { const source = index.text.replace(EDITOR_MARKER, ''); const dataSource = await getDatastreamsForIntegration(source); + const newDefinitions = buildSourcesDefinitions( dataSource?.dataStreams?.map(({ name }) => ({ name, isIntegration: false })) || [] ); - suggestions.push(...newDefinitions); + suggestions.push( + ...(canRemoveQuote ? removeQuoteForSuggestedSources(newDefinitions) : newDefinitions) + ); } else { // FROM // @TODO: filter down the suggestions here based on other existing sources defined const sourcesDefinitions = await getSources(); - suggestions.push(...sourcesDefinitions); + suggestions.push( + ...(canRemoveQuote + ? removeQuoteForSuggestedSources(sourcesDefinitions) + : sourcesDefinitions) + ); } } } diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts index 53e65c2f95aba..df1da9d092d73 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts @@ -12,14 +12,14 @@ import { groupingFunctionDefinitions } from '../definitions/grouping'; import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; import { evalFunctionDefinitions } from '../definitions/functions'; import { getFunctionSignatures, getCommandSignature } from '../definitions/helpers'; -import { chronoLiterals, timeUnitsToSuggest } from '../definitions/literals'; +import { timeUnitsToSuggest } from '../definitions/literals'; import { FunctionDefinition, CommandDefinition, CommandOptionsDefinition, CommandModeDefinition, } from '../definitions/types'; -import { getCommandDefinition, shouldBeQuotedText } from '../shared/helpers'; +import { shouldBeQuotedSource, getCommandDefinition, shouldBeQuotedText } from '../shared/helpers'; import { buildDocumentation, buildFunctionDocumentation } from './documentation_util'; import { DOUBLE_BACKTICK, SINGLE_TICK_REGEX } from '../shared/constants'; @@ -37,6 +37,13 @@ function getSafeInsertText(text: string, options: { dashSupported?: boolean } = ? `\`${text.replace(SINGLE_TICK_REGEX, DOUBLE_BACKTICK)}\`` : text; } +export function getQuotedText(text: string) { + return text.startsWith(`"`) && text.endsWith(`"`) ? text : `"${text}"`; +} + +function getSafeInsertSourceText(text: string) { + return shouldBeQuotedSource(text) ? getQuotedText(text) : text; +} export function getSuggestionFunctionDefinition(fn: FunctionDefinition): SuggestionRawDefinition { const fullSignatures = getFunctionSignatures(fn); @@ -148,7 +155,7 @@ export const buildSourcesDefinitions = ( ): SuggestionRawDefinition[] => sources.map(({ name, isIntegration, title }) => ({ label: title ?? name, - text: name, + text: getSafeInsertSourceText(name), isSnippet: isIntegration, ...(isIntegration && { command: TRIGGER_SUGGESTION_COMMAND }), kind: isIntegration ? 'Class' : 'Issue', @@ -336,9 +343,6 @@ export function getCompatibleLiterals(commandName: string, types: string[], name if (types.includes('time_literal_unit')) { suggestions.push(...buildConstantsDefinitions(timeUnitsToSuggest.map(({ name }) => name))); // i.e. year, month, ... } - if (types.includes('chrono_literal')) { - suggestions.push(...buildConstantsDefinitions(chronoLiterals.map(({ name }) => name))); // i.e. EPOC_DAY, ... - } if (types.includes('string')) { if (names) { const index = types.indexOf('string'); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts index fa6e364c78ca9..f9586670b29e6 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/helper.ts @@ -7,8 +7,9 @@ */ import type { ESQLAstItem, ESQLCommand, ESQLFunction, ESQLSource } from '@kbn/esql-ast'; -import { FunctionDefinition } from '../definitions/types'; +import type { FunctionDefinition } from '../definitions/types'; import { getFunctionDefinition, isAssignment, isFunctionItem } from '../shared/helpers'; +import type { SuggestionRawDefinition } from './types'; function extractFunctionArgs(args: ESQLAstItem[]): ESQLFunction[] { return args.flatMap((arg) => (isAssignment(arg) ? arg.args[1] : arg)).filter(isFunctionItem); @@ -71,3 +72,11 @@ export function getSourcesFromCommands(commands: ESQLCommand[], sourceType: 'ind return sources.length === 1 ? sources[0] : undefined; } + +export function removeQuoteForSuggestedSources(suggestions: SuggestionRawDefinition[]) { + return suggestions.map((d) => ({ + ...d, + // "text" -> text + text: d.text.startsWith('"') && d.text.endsWith('"') ? d.text.slice(1, -1) : d.text, + })); +} diff --git a/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.ts b/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.ts index 2c8620223ebb3..f6469a736a0e8 100644 --- a/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.ts +++ b/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import { i18n } from '@kbn/i18n'; -import levenshtein from 'js-levenshtein'; +import { distance } from 'fastest-levenshtein'; import type { AstProviderFn, ESQLAst, EditorError, ESQLMessage } from '@kbn/esql-ast'; import { uniqBy } from 'lodash'; import { @@ -90,8 +90,7 @@ function createAction(title: string, solution: string, error: EditorError): Code async function getSpellingPossibilities(fn: () => Promise, errorText: string) { const allPossibilities = await fn(); const allSolutions = allPossibilities.reduce((solutions, item) => { - const distance = levenshtein(item, errorText); - if (distance < 3) { + if (distance(item, errorText) < 3) { solutions.push(item); } return solutions; @@ -306,8 +305,8 @@ async function getSpellingActionForMetadata( ) { const errorText = queryString.substring(error.startColumn - 1, error.endColumn - 1); const allSolutions = METADATA_FIELDS.reduce((solutions, item) => { - const distance = levenshtein(item, errorText); - if (distance < 3) { + const dist = distance(item, errorText); + if (dist < 3) { solutions.push(item); } return solutions; diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts index ceb5789e0dd39..87c9f82360b44 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts @@ -668,8 +668,40 @@ const dateExtractDefinition: FunctionDefinition = { params: [ { name: 'datePart', - type: 'chrono_literal', + type: 'string', optional: false, + literalOptions: [ + 'ALIGNED_DAY_OF_WEEK_IN_MONTH', + 'ALIGNED_DAY_OF_WEEK_IN_YEAR', + 'ALIGNED_WEEK_OF_MONTH', + 'ALIGNED_WEEK_OF_YEAR', + 'AMPM_OF_DAY', + 'CLOCK_HOUR_OF_AMPM', + 'CLOCK_HOUR_OF_DAY', + 'DAY_OF_MONTH', + 'DAY_OF_WEEK', + 'DAY_OF_YEAR', + 'EPOCH_DAY', + 'ERA', + 'HOUR_OF_AMPM', + 'HOUR_OF_DAY', + 'INSTANT_SECONDS', + 'MICRO_OF_DAY', + 'MICRO_OF_SECOND', + 'MILLI_OF_DAY', + 'MILLI_OF_SECOND', + 'MINUTE_OF_DAY', + 'MINUTE_OF_HOUR', + 'MONTH_OF_YEAR', + 'NANO_OF_DAY', + 'NANO_OF_SECOND', + 'OFFSET_SECONDS', + 'PROLEPTIC_MONTH', + 'SECOND_OF_DAY', + 'SECOND_OF_MINUTE', + 'YEAR', + 'YEAR_OF_ERA', + ], }, { name: 'date', diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.test.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.test.ts new file mode 100644 index 0000000000000..87eb31de4d3c9 --- /dev/null +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.test.ts @@ -0,0 +1,48 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { shouldBeQuotedSource } from './helpers'; + +describe('shouldBeQuotedSource', () => { + it('does not have to be quoted for sources with acceptable characters @-+$', () => { + expect(shouldBeQuotedSource('foo')).toBe(false); + expect(shouldBeQuotedSource('123-test@foo_bar+baz1')).toBe(false); + expect(shouldBeQuotedSource('my-index*')).toBe(false); + expect(shouldBeQuotedSource('my-index$')).toBe(false); + expect(shouldBeQuotedSource('.my-index$')).toBe(false); + }); + it(`should be quoted if containing any of special characters [:"=|,[\]/ \t\r\n]`, () => { + expect(shouldBeQuotedSource('foo\ttest')).toBe(true); + expect(shouldBeQuotedSource('foo\rtest')).toBe(true); + expect(shouldBeQuotedSource('foo\ntest')).toBe(true); + expect(shouldBeQuotedSource('foo:test=bar')).toBe(true); + expect(shouldBeQuotedSource('foo|test=bar')).toBe(true); + expect(shouldBeQuotedSource('foo[test]=bar')).toBe(true); + expect(shouldBeQuotedSource('foo/test=bar')).toBe(true); + expect(shouldBeQuotedSource('foo test=bar')).toBe(true); + expect(shouldBeQuotedSource('foo,test-*,abc')).toBe(true); + expect(shouldBeQuotedSource('foo, test-*, abc, xyz')).toBe(true); + expect(shouldBeQuotedSource('foo, test-*, abc, xyz,test123')).toBe(true); + expect(shouldBeQuotedSource('foo,test,xyz')).toBe(true); + expect( + shouldBeQuotedSource(',') + ).toBe(true); + expect(shouldBeQuotedSource('`backtick`,``multiple`back``ticks```')).toBe(true); + expect(shouldBeQuotedSource('test,metadata,metaata,.metadata')).toBe(true); + expect(shouldBeQuotedSource('cluster:index')).toBe(true); + expect(shouldBeQuotedSource('cluster:index|pattern')).toBe(true); + expect(shouldBeQuotedSource('cluster:.index')).toBe(true); + expect(shouldBeQuotedSource('cluster*:index*')).toBe(true); + expect(shouldBeQuotedSource('cluster*:*')).toBe(true); + expect(shouldBeQuotedSource('*:index*')).toBe(true); + expect(shouldBeQuotedSource('*:index|pattern')).toBe(true); + expect(shouldBeQuotedSource('*:*')).toBe(true); + expect(shouldBeQuotedSource('*:*,cluster*:index|pattern,i|p')).toBe(true); + expect(shouldBeQuotedSource('index-[dd-mm]')).toBe(true); + }); +}); diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts index effd6b1b16ddd..9bdb1f4e416a1 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts @@ -17,14 +17,14 @@ import type { ESQLSource, ESQLTimeInterval, } from '@kbn/esql-ast'; -import { ESQLInlineCast } from '@kbn/esql-ast/src/types'; +import { ESQLInlineCast, ESQLParamLiteral } from '@kbn/esql-ast/src/types'; import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; import { builtinFunctions } from '../definitions/builtin'; import { commandDefinitions } from '../definitions/commands'; import { evalFunctionDefinitions } from '../definitions/functions'; import { groupingFunctionDefinitions } from '../definitions/grouping'; import { getFunctionSignatures } from '../definitions/helpers'; -import { timeUnits, chronoLiterals } from '../definitions/literals'; +import { timeUnits } from '../definitions/literals'; import { byOption, metadataOption, @@ -233,9 +233,6 @@ function compareLiteralType(argType: string, item: ESQLLiteral) { return false; } - if (argType === 'chrono_literal') { - return chronoLiterals.some(({ name }) => name === item.text); - } // date-type parameters accept string literals because of ES auto-casting return ['string', 'date'].includes(argType); } @@ -407,7 +404,7 @@ export function checkFunctionArgMatchesDefinition( parentCommand?: string ) { const argType = parameterDefinition.type; - if (argType === 'any') { + if (argType === 'any' || isParam(arg)) { return true; } if (arg.type === 'literal') { @@ -566,6 +563,10 @@ export function isRestartingExpression(text: string) { return getLastCharFromTrimmed(text) === ','; } +export function shouldBeQuotedSource(text: string) { + // Based on lexer `fragment UNQUOTED_SOURCE_PART` + return /[:"=|,[\]\/ \t\r\n]/.test(text); +} export function shouldBeQuotedText( text: string, { dashSupported }: { dashSupported?: boolean } = {} @@ -575,3 +576,9 @@ export function shouldBeQuotedText( export const isAggFunction = (arg: ESQLFunction): boolean => getFunctionDefinition(arg.name)?.type === 'agg'; + +export const isParam = (x: unknown): x is ESQLParamLiteral => + !!x && + typeof x === 'object' && + (x as ESQLParamLiteral).type === 'literal' && + (x as ESQLParamLiteral).literalType === 'param'; diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts index 74301ced77e63..06d23a30c441d 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.from.ts @@ -20,7 +20,7 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => { "SyntaxError: mismatched input 'f' expecting {'explain', 'from', 'meta', 'metrics', 'row', 'show'}", ]); await expectErrors('from ', [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''", + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}", ]); }); @@ -30,6 +30,8 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => { await expectErrors('from index', []); await expectErrors('FROM index', []); + await expectErrors('FROM "index"', []); + await expectErrors('FROM """index"""', []); await expectErrors('FrOm index', []); await expectErrors('from index, other_index', []); await expectErrors('from index, other_index,.secret_index', []); @@ -65,10 +67,10 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from index,', [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''", + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}", ]); await expectErrors(`FROM index\n, \tother_index\t,\n \t `, [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''", + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}", ]); await expectErrors(`from assignment = 1`, [ @@ -80,10 +82,7 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => { test('errors on invalid syntax', async () => { const { expectErrors } = await setup(); - await expectErrors('FROM `index`', [ - "SyntaxError: token recognition error at: '`'", - "SyntaxError: token recognition error at: '`'", - ]); + await expectErrors('FROM `index`', ['Unknown index [`index`]']); await expectErrors(`from assignment = 1`, [ "SyntaxError: mismatched input '=' expecting ", 'Unknown index [assignment]', diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts index 5d1ddb0865ea5..44c15c722a1de 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts @@ -19,7 +19,7 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => { "SyntaxError: mismatched input 'm' expecting {'explain', 'from', 'meta', 'metrics', 'row', 'show'}", ]); await expectErrors('metrics ', [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''", + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}", ]); }); @@ -61,10 +61,10 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('metrics index,', [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''", + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}", ]); await expectErrors(`metrics index\n, \tother_index\t,\n \t `, [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''", + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}", ]); }); @@ -75,10 +75,7 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => { "SyntaxError: token recognition error at: '='", "SyntaxError: token recognition error at: '1'", ]); - await expectErrors('metrics `index`', [ - "SyntaxError: token recognition error at: '`'", - "SyntaxError: token recognition error at: '`'", - ]); + await expectErrors('metrics `index`', ['Unknown index [`index`]']); }); test('errors on unknown index', async () => { diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.params.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.params.test.ts new file mode 100644 index 0000000000000..17f91a72c52a4 --- /dev/null +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.params.test.ts @@ -0,0 +1,43 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { setup } from './helpers'; + +test('should allow param inside agg function argument', async () => { + const { validate } = await setup(); + + const res1 = await validate('FROM index | STATS avg(?)'); + const res2 = await validate('FROM index | STATS avg(?named)'); + const res3 = await validate('FROM index | STATS avg(?123)'); + + expect(res1).toMatchObject({ errors: [], warnings: [] }); + expect(res2).toMatchObject({ errors: [], warnings: [] }); + expect(res3).toMatchObject({ errors: [], warnings: [] }); +}); + +test('allow params in WHERE command expressions', async () => { + const { validate } = await setup(); + + const res1 = await validate('FROM index | WHERE stringField >= ?earliest'); + const res2 = await validate(` + FROM index + | WHERE stringField >= ?earliest + | WHERE stringField <= ?0 + | WHERE stringField == ? + `); + const res3 = await validate(` + FROM index + | WHERE stringField >= ?earliest + AND stringField <= ?0 + AND stringField == ? + `); + + expect(res1).toMatchObject({ errors: [], warnings: [] }); + expect(res2).toMatchObject({ errors: [], warnings: [] }); + expect(res3).toMatchObject({ errors: [], warnings: [] }); +}); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index daf8d7c6de862..7f70b266458f7 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -8662,7 +8662,8 @@ { "query": "from a_index | enrich `this``is fine`", "error": [ - "SyntaxError: mismatched input '`this``is fine`' expecting ENRICH_POLICY_NAME" + "SyntaxError: extraneous input 'fine`' expecting ", + "Unknown policy [`this``is]" ], "warning": [] }, @@ -11005,6 +11006,13 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval date_extract(\"SOME_RANDOM_STRING\", now())", + "error": [], + "warning": [ + "Invalid option [\"SOME_RANDOM_STRING\"] for date_extract. Supported options: [\"ALIGNED_DAY_OF_WEEK_IN_MONTH\", \"ALIGNED_DAY_OF_WEEK_IN_YEAR\", \"ALIGNED_WEEK_OF_MONTH\", \"ALIGNED_WEEK_OF_YEAR\", \"AMPM_OF_DAY\", \"CLOCK_HOUR_OF_AMPM\", \"CLOCK_HOUR_OF_DAY\", \"DAY_OF_MONTH\", \"DAY_OF_WEEK\", \"DAY_OF_YEAR\", \"EPOCH_DAY\", \"ERA\", \"HOUR_OF_AMPM\", \"HOUR_OF_DAY\", \"INSTANT_SECONDS\", \"MICRO_OF_DAY\", \"MICRO_OF_SECOND\", \"MILLI_OF_DAY\", \"MILLI_OF_SECOND\", \"MINUTE_OF_DAY\", \"MINUTE_OF_HOUR\", \"MONTH_OF_YEAR\", \"NANO_OF_DAY\", \"NANO_OF_SECOND\", \"OFFSET_SECONDS\", \"PROLEPTIC_MONTH\", \"SECOND_OF_DAY\", \"SECOND_OF_MINUTE\", \"YEAR\", \"YEAR_OF_ERA\"]." + ] + }, { "query": "from a_index | eval var = date_extract(\"ALIGNED_DAY_OF_WEEK_IN_MONTH\", dateField)", "error": [], @@ -11023,7 +11031,6 @@ { "query": "from a_index | eval date_extract(stringField, stringField)", "error": [ - "Argument of [date_extract] must be [chrono_literal], found value [stringField] type [string]", "Argument of [date_extract] must be [date], found value [stringField] type [string]" ], "warning": [] @@ -11043,7 +11050,7 @@ { "query": "row var = date_extract(true, true)", "error": [ - "Argument of [date_extract] must be [chrono_literal], found value [true] type [boolean]", + "Argument of [date_extract] must be [string], found value [true] type [boolean]", "Argument of [date_extract] must be [date], found value [true] type [boolean]" ], "warning": [] @@ -11056,7 +11063,7 @@ { "query": "from a_index | eval date_extract(booleanField, booleanField)", "error": [ - "Argument of [date_extract] must be [chrono_literal], found value [booleanField] type [boolean]", + "Argument of [date_extract] must be [string], found value [booleanField] type [boolean]", "Argument of [date_extract] must be [date], found value [booleanField] type [boolean]" ], "warning": [] @@ -26377,7 +26384,7 @@ { "query": "from ", "error": [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''" + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}" ], "warning": [] }, @@ -26391,6 +26398,16 @@ "error": [], "warning": [] }, + { + "query": "FROM \"index\"", + "error": [], + "warning": [] + }, + { + "query": "FROM \"\"\"index\"\"\"", + "error": [], + "warning": [] + }, { "query": "FrOm index", "error": [], @@ -26539,14 +26556,14 @@ { "query": "from index,", "error": [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''" + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}" ], "warning": [] }, { "query": "FROM index\n, \tother_index\t,\n \t ", "error": [ - "SyntaxError: missing INDEX_UNQUOTED_IDENTIFIER at ''" + "SyntaxError: mismatched input '' expecting {UNQUOTED_SOURCE, QUOTED_STRING}" ], "warning": [] }, @@ -26561,8 +26578,7 @@ { "query": "FROM `index`", "error": [ - "SyntaxError: token recognition error at: '`'", - "SyntaxError: token recognition error at: '`'" + "Unknown index [`index`]" ], "warning": [] }, diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index 6c1b66574862c..062e0b9ae78f1 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -12,7 +12,7 @@ import { ignoreErrorsMap, validateQuery } from './validation'; import { evalFunctionDefinitions } from '../definitions/functions'; import { getFunctionSignatures } from '../definitions/helpers'; import { FunctionDefinition, SupportedFieldType, supportedFieldTypes } from '../definitions/types'; -import { chronoLiterals, timeUnits, timeUnitsToSuggest } from '../definitions/literals'; +import { timeUnits, timeUnitsToSuggest } from '../definitions/literals'; import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; import capitalize from 'lodash/capitalize'; import { camelCase } from 'lodash'; @@ -58,13 +58,9 @@ const nestedFunctions = { }; const literals = { - chrono_literal: chronoLiterals[0].name, time_literal: timeUnitsToSuggest[0].name, }; -function getLiteralType(typeString: 'chrono_literal' | 'time_literal') { - if (typeString === 'chrono_literal') { - return literals[typeString]; - } +function getLiteralType(typeString: 'time_literal') { return `1 ${literals[typeString]}`; } @@ -144,7 +140,7 @@ function getFieldMapping( } if (/literal$/.test(typeString) && useLiterals) { return { - name: getLiteralType(typeString as 'chrono_literal' | 'time_literal'), + name: getLiteralType(typeString as 'time_literal'), type, ...rest, }; @@ -1359,7 +1355,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings(`from a_index | enrich policy `, []); testErrorsAndWarnings('from a_index | enrich `this``is fine`', [ - "SyntaxError: mismatched input '`this``is fine`' expecting ENRICH_POLICY_NAME", + "SyntaxError: extraneous input 'fine`' expecting ", + 'Unknown policy [`this``is]', ]); testErrorsAndWarnings('from a_index | enrich this is fine', [ "SyntaxError: mismatched input 'is' expecting ", @@ -2609,6 +2606,13 @@ describe('validation logic', () => { describe('date_extract', () => { testErrorsAndWarnings('row var = date_extract("ALIGNED_DAY_OF_WEEK_IN_MONTH", now())', []); testErrorsAndWarnings('row date_extract("ALIGNED_DAY_OF_WEEK_IN_MONTH", now())', []); + testErrorsAndWarnings( + 'from a_index | eval date_extract("SOME_RANDOM_STRING", now())', + [], + [ + 'Invalid option ["SOME_RANDOM_STRING"] for date_extract. Supported options: ["ALIGNED_DAY_OF_WEEK_IN_MONTH", "ALIGNED_DAY_OF_WEEK_IN_YEAR", "ALIGNED_WEEK_OF_MONTH", "ALIGNED_WEEK_OF_YEAR", "AMPM_OF_DAY", "CLOCK_HOUR_OF_AMPM", "CLOCK_HOUR_OF_DAY", "DAY_OF_MONTH", "DAY_OF_WEEK", "DAY_OF_YEAR", "EPOCH_DAY", "ERA", "HOUR_OF_AMPM", "HOUR_OF_DAY", "INSTANT_SECONDS", "MICRO_OF_DAY", "MICRO_OF_SECOND", "MILLI_OF_DAY", "MILLI_OF_SECOND", "MINUTE_OF_DAY", "MINUTE_OF_HOUR", "MONTH_OF_YEAR", "NANO_OF_DAY", "NANO_OF_SECOND", "OFFSET_SECONDS", "PROLEPTIC_MONTH", "SECOND_OF_DAY", "SECOND_OF_MINUTE", "YEAR", "YEAR_OF_ERA"].', + ] + ); testErrorsAndWarnings( 'from a_index | eval var = date_extract("ALIGNED_DAY_OF_WEEK_IN_MONTH", dateField)', @@ -2626,7 +2630,6 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | eval date_extract(stringField, stringField)', [ - 'Argument of [date_extract] must be [chrono_literal], found value [stringField] type [string]', 'Argument of [date_extract] must be [date], found value [stringField] type [string]', ]); @@ -2641,7 +2644,7 @@ describe('validation logic', () => { ); testErrorsAndWarnings('row var = date_extract(true, true)', [ - 'Argument of [date_extract] must be [chrono_literal], found value [true] type [boolean]', + 'Argument of [date_extract] must be [string], found value [true] type [boolean]', 'Argument of [date_extract] must be [date], found value [true] type [boolean]', ]); @@ -2651,7 +2654,7 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | eval date_extract(booleanField, booleanField)', [ - 'Argument of [date_extract] must be [chrono_literal], found value [booleanField] type [boolean]', + 'Argument of [date_extract] must be [string], found value [booleanField] type [boolean]', 'Argument of [date_extract] must be [date], found value [booleanField] type [boolean]', ]); testErrorsAndWarnings('from a_index | eval date_extract(null, null)', []); diff --git a/packages/kbn-field-types/src/kbn_field_types.ts b/packages/kbn-field-types/src/kbn_field_types.ts index 7ec22de078230..5059d37da1d0e 100644 --- a/packages/kbn-field-types/src/kbn_field_types.ts +++ b/packages/kbn-field-types/src/kbn_field_types.ts @@ -50,6 +50,10 @@ export const getFilterableKbnTypeNames = (): string[] => registeredKbnTypes.filter((type) => type.filterable).map((type) => type.name); export function esFieldTypeToKibanaFieldType(type: string) { + // 'counter_integer', 'counter_long', 'counter_double'... + if (type.startsWith('counter_')) { + return KBN_FIELD_TYPES.NUMBER; + } switch (type) { case ES_FIELD_TYPES._INDEX: return KBN_FIELD_TYPES.STRING; diff --git a/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.test.tsx b/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.test.tsx index 1311d1c5bbc2f..ef9b0572c689d 100644 --- a/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.test.tsx +++ b/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.test.tsx @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { type DataViewField } from '@kbn/data-views-plugin/common'; import { fieldNameWildcardMatcher, getFieldSearchMatchingHighlight, @@ -16,54 +15,107 @@ const name = 'test.this_value.maybe'; describe('fieldNameWildcardMatcher', function () { describe('fieldNameWildcardMatcher()', () => { it('should work correctly with wildcard', async () => { - expect(fieldNameWildcardMatcher({ displayName: 'test' } as DataViewField, 'no')).toBe(false); - expect( - fieldNameWildcardMatcher({ displayName: 'test', name: 'yes' } as DataViewField, 'yes') - ).toBe(true); + expect(fieldNameWildcardMatcher({ displayName: 'test', name: 'test' }, 'no')).toBe(false); + expect(fieldNameWildcardMatcher({ displayName: 'test', name: 'yes' }, 'yes')).toBe(true); const search = 'test*ue'; - expect(fieldNameWildcardMatcher({ displayName: 'test' } as DataViewField, search)).toBe( - false - ); - expect(fieldNameWildcardMatcher({ displayName: 'test.value' } as DataViewField, search)).toBe( - true - ); - expect(fieldNameWildcardMatcher({ name: 'test.this_value' } as DataViewField, search)).toBe( - true - ); - expect(fieldNameWildcardMatcher({ name: 'message.test' } as DataViewField, search)).toBe( - false - ); - expect(fieldNameWildcardMatcher({ name } as DataViewField, search)).toBe(false); - expect(fieldNameWildcardMatcher({ name } as DataViewField, `${search}*`)).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, '*value*')).toBe(true); + expect(fieldNameWildcardMatcher({ displayName: 'test', name: 'test' }, search)).toBe(false); + expect( + fieldNameWildcardMatcher({ displayName: 'test.value', name: 'test.value' }, search) + ).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'test.this_value' }, search)).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'message.test' }, search)).toBe(false); + expect(fieldNameWildcardMatcher({ name }, search)).toBe(false); + expect(fieldNameWildcardMatcher({ name }, `${search}*`)).toBe(true); + expect(fieldNameWildcardMatcher({ name }, '*value*')).toBe(true); }); it('should work correctly with spaces', async () => { - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'test maybe ')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'test maybe*')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'test. this')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'this _value be')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'test')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'this')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, ' value ')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'be')).toBe(true); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'test this here')).toBe(false); - expect(fieldNameWildcardMatcher({ name } as DataViewField, 'test that')).toBe(false); - expect(fieldNameWildcardMatcher({ name } as DataViewField, ' ')).toBe(false); - expect(fieldNameWildcardMatcher({ name: 'geo.location3' } as DataViewField, '3')).toBe(true); - expect(fieldNameWildcardMatcher({ name: 'geo_location3' } as DataViewField, 'geo 3')).toBe( - true - ); + expect(fieldNameWildcardMatcher({ name }, 'test maybe ')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, 'test maybe*')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, 'test. this')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, 'this _value be')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, 'test')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, 'this')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, ' value ')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, 'be')).toBe(true); + expect(fieldNameWildcardMatcher({ name }, 'test this here')).toBe(false); + expect(fieldNameWildcardMatcher({ name }, 'test that')).toBe(false); + expect(fieldNameWildcardMatcher({ name }, ' ')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'geo.location3' }, '3')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'geo_location3' }, 'geo 3')).toBe(true); }); it('should be case-insensitive', async () => { - expect(fieldNameWildcardMatcher({ name: 'Test' } as DataViewField, 'test')).toBe(true); - expect(fieldNameWildcardMatcher({ name: 'test' } as DataViewField, 'Test')).toBe(true); - expect(fieldNameWildcardMatcher({ name: 'tesT' } as DataViewField, 'Tes*')).toBe(true); - expect(fieldNameWildcardMatcher({ name: 'tesT' } as DataViewField, 'tes*')).toBe(true); - expect(fieldNameWildcardMatcher({ name: 'tesT' } as DataViewField, 't T')).toBe(true); - expect(fieldNameWildcardMatcher({ name: 'tesT' } as DataViewField, 't t')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'Test' }, 'test')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'test' }, 'Test')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'tesT' }, 'Tes*')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'tesT' }, 'tes*')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'tesT' }, 't T')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'tesT' }, 't t')).toBe(true); + }); + + describe('fuzzy search', () => { + test('only matches strings longer than 3 characters', () => { + expect(fieldNameWildcardMatcher({ name: 'a' }, 'b')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'ab' }, 'cb')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'abc' }, 'abb')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'abcd' }, 'abbd')).toBe(true); + }); + test('is case insensitive', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefg' }, 'AAbcdef')).toBe(true); + }); + test('tests both displayName and name', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefg' }, 'aabcdefg')).toBe(true); + expect( + fieldNameWildcardMatcher({ name: 'abcdefg', displayName: 'irrelevantstring' }, 'bbcdefg') + ).toBe(true); + expect( + fieldNameWildcardMatcher({ name: 'irrelevantstring', displayName: 'abcdefg' }, 'bbcdefg') + ).toBe(true); + }); + test('finds matches with a typo at the beginning of the string', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmno' }, '.bcdefghijklmno')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmno' }, '.bcde')).toBe(true); + }); + test('finds matches with a typo in the middle of the string', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmno' }, 'abcdefghi.klmno')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmno' }, 'ghi.klm')).toBe(true); + }); + test('finds matches with a typo at the end of the string', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmno' }, 'abcdefghijklmn.')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmno' }, 'klmn.')).toBe(true); + }); + test('finds matches with an additional character at the beginning of the string', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, '.abcdefghijklmn')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, '.abcde')).toBe(true); + }); + test('finds matches with an additional character in the middle of the string', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, 'abcdefgh.ijklmn')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, 'fgh.ijklm')).toBe(true); + }); + test('finds matches with an additional character at the end of the string', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, 'abcdefghijklmn.')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, 'ghijklmn.')).toBe(true); + }); + test('finds matches with a missing character in the middle of the string', () => { + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, 'abcdefgijklmn')).toBe(true); + expect(fieldNameWildcardMatcher({ name: 'abcdefghijklmn' }, 'gijkl')).toBe(true); + }); + test('does not find matches exceeding edit distance 1', () => { + // swapping edit distance = 2 + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'abdcefhghijklm')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'abdce')).toBe(false); + // 2 char removed + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'abcfhghijklm')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'abcfhg')).toBe(false); + // 2 chars added + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'abcfhghijklmmm')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'hijklmmm')).toBe(false); + // 2 typos + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'cccdefhghijklm')).toBe(false); + expect(fieldNameWildcardMatcher({ name: 'abcdefhghijklm' }, 'cccdefh')).toBe(false); + }); }); }); @@ -74,10 +126,10 @@ describe('fieldNameWildcardMatcher', function () { expect(getFieldSearchMatchingHighlight('test this')).toBe(''); }); - it('should correctly return a full match for a wildcard search', async () => { - expect(getFieldSearchMatchingHighlight('Test this', 'test*')).toBe('Test this'); - expect(getFieldSearchMatchingHighlight('test this', '*this')).toBe('test this'); - expect(getFieldSearchMatchingHighlight('test this', ' te th')).toBe('test this'); + it('should correctly return a match for a wildcard search', async () => { + expect(getFieldSearchMatchingHighlight('Test this', 'test*')).toBe('test'); + expect(getFieldSearchMatchingHighlight('test this', '*this')).toBe(' this'); + expect(getFieldSearchMatchingHighlight('test this', ' te th')).toBe('t th'); }); }); }); diff --git a/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.ts b/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.ts index ba06e36e95c9f..472f377ccddae 100644 --- a/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.ts +++ b/packages/kbn-field-utils/src/utils/field_name_wildcard_matcher.ts @@ -7,6 +7,7 @@ */ import { escapeRegExp, memoize } from 'lodash'; +import { distance } from 'fastest-levenshtein'; const makeRegEx = memoize(function makeRegEx(glob: string) { const trimmedGlob = glob.trim(); @@ -21,7 +22,7 @@ const makeRegEx = memoize(function makeRegEx(glob: string) { globRegex = '.*' + globRegex + '.*'; } - return new RegExp(globRegex.includes('*') ? `^${globRegex}$` : globRegex, 'i'); + return new RegExp(globRegex.includes('*') ? `^${globRegex}$` : globRegex); }); /** @@ -37,11 +38,61 @@ export const fieldNameWildcardMatcher = ( if (!fieldSearchHighlight?.trim()) { return false; } + const searchLower = fieldSearchHighlight.toLowerCase(); + const displayNameLower = field.displayName?.toLowerCase(); + const nameLower = field.name.toLowerCase(); - const regExp = makeRegEx(fieldSearchHighlight); - return (!!field.displayName && regExp.test(field.displayName)) || regExp.test(field.name); + const regExp = makeRegEx(searchLower); + const doesWildcardMatch = + (!!displayNameLower && regExp.test(displayNameLower)) || regExp.test(nameLower); + if (doesWildcardMatch) { + return true; + } + + if (searchLower.length < FUZZY_STRING_MIN_LENGTH) { + return false; + } + return testFuzzySearch({ name: nameLower, displayName: displayNameLower }, searchLower); }; +const FUZZY_STRING_MIN_LENGTH = 4; +const FUZZY_SEARCH_DISTANCE = 1; + +const testFuzzySearch = (field: { name: string; displayName?: string }, searchValue: string) => { + return ( + Boolean(testFuzzySearchForString(field.displayName, searchValue)) || + (field.name !== field.displayName && Boolean(testFuzzySearchForString(field.name, searchValue))) + ); +}; + +const testFuzzySearchForString = (label: string | undefined, searchValue: string) => { + if (!label || label.length < searchValue.length - 2) { + return false; + } + + const substrLength = Math.max( + Math.min(searchValue.length, label.length), + FUZZY_STRING_MIN_LENGTH + ); + + // performance optimization: instead of building the whole matrix, + // only iterate through the strings of the substring length +- 1 character, + // for example for searchValue = 'test' and label = 'test_value', + // we iterate through 'test', 'est_', 'st_v' (and +- character cases too). + const iterationsCount = label.length - substrLength + 1; + for (let i = 0; i <= iterationsCount; i++) { + for (let j = substrLength - 1; j <= substrLength + 1; j++) { + if (compareLevenshtein(searchValue, label.substring(i, j + i))) { + return label.substring(i, j + i); + } + } + } + return false; +}; + +const compareLevenshtein = (str1: string, str2: string) => + distance(str1, str2) <= FUZZY_SEARCH_DISTANCE; + /** * Adapts fieldNameWildcardMatcher to combobox props. * @param field @@ -64,13 +115,15 @@ export function getFieldSearchMatchingHighlight( displayName: string, fieldSearchHighlight?: string ): string { + if (!fieldSearchHighlight) { + return ''; + } const searchHighlight = (fieldSearchHighlight || '').trim(); - if ( - (searchHighlight.includes('*') || searchHighlight.includes(' ')) && - fieldNameWildcardMatcher({ name: displayName }, searchHighlight) - ) { - return displayName; + if (displayName.toLowerCase().indexOf(searchHighlight.toLowerCase()) > -1) { + return searchHighlight; } - - return searchHighlight; + return ( + testFuzzySearchForString(displayName.toLowerCase(), searchHighlight.toLowerCase()) || + displayName + ); } diff --git a/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts b/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts index 0ccad94674208..3c37b9b01c3fb 100644 --- a/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts +++ b/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts @@ -7,6 +7,7 @@ */ import type { DatatableColumnMeta } from '@kbn/expressions-plugin/common'; +import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils'; import { getFieldIconType } from './get_field_icon_type'; export function getTextBasedColumnIconType( @@ -19,10 +20,8 @@ export function getTextBasedColumnIconType( | null ): string | null { return columnMeta && columnMeta.type - ? getFieldIconType({ - name: '', - type: columnMeta.type, - esTypes: columnMeta.esType ? [columnMeta.esType] : [], - }) + ? getFieldIconType( + convertDatatableColumnToDataViewFieldSpec({ id: '', name: '', meta: columnMeta }) + ) : null; } diff --git a/packages/kbn-field-utils/tsconfig.json b/packages/kbn-field-utils/tsconfig.json index 4b75159b5f7fe..9ac5ba7e942bc 100644 --- a/packages/kbn-field-utils/tsconfig.json +++ b/packages/kbn-field-utils/tsconfig.json @@ -10,6 +10,7 @@ "@kbn/react-field", "@kbn/field-types", "@kbn/expressions-plugin", + "@kbn/data-view-utils", ], "exclude": ["target/**/*"] } diff --git a/packages/kbn-monaco/src/esql/language.ts b/packages/kbn-monaco/src/esql/language.ts index 4e486a5c24188..7c49da41a996e 100644 --- a/packages/kbn-monaco/src/esql/language.ts +++ b/packages/kbn-monaco/src/esql/language.ts @@ -39,11 +39,13 @@ export const ESQLLang: CustomLangModuleType = { { open: '(', close: ')' }, { open: '[', close: ']' }, { open: `'`, close: `'` }, + { open: '"""', close: '"""' }, { open: '"', close: '"' }, ], surroundingPairs: [ { open: '(', close: ')' }, { open: `'`, close: `'` }, + { open: '"""', close: '"""' }, { open: '"', close: '"' }, ], }, diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 00bcbf6481939..3b7828dc7f67d 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -42,6 +42,7 @@ pageLoadAssetSize: embeddableEnhanced: 22107 enterpriseSearch: 66810 entityManager: 17175 + esql: 37000 esqlDataGrid: 24582 esUiShared: 326654 eventAnnotation: 30000 @@ -156,7 +157,6 @@ pageLoadAssetSize: synthetics: 40958 telemetry: 51957 telemetryManagementSection: 38586 - textBasedLanguages: 37000 threatIntelligence: 44299 timelines: 327300 transform: 41007 diff --git a/packages/kbn-recently-accessed/README.md b/packages/kbn-recently-accessed/README.md new file mode 100644 index 0000000000000..a7579822c9123 --- /dev/null +++ b/packages/kbn-recently-accessed/README.md @@ -0,0 +1,4 @@ +# @kbn/recently-accessed + +The `RecentlyAccessedService` uses browser local storage to manage records of recently accessed objects. +This can be used to make recent items easier for users to find in listing UIs. diff --git a/packages/kbn-recently-accessed/index.ts b/packages/kbn-recently-accessed/index.ts new file mode 100644 index 0000000000000..3b23a548ce946 --- /dev/null +++ b/packages/kbn-recently-accessed/index.ts @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export { + type RecentlyAccessed, + type RecentlyAccessedHistoryItem, + RecentlyAccessedService, +} from './src'; diff --git a/packages/kbn-recently-accessed/jest.config.js b/packages/kbn-recently-accessed/jest.config.js new file mode 100644 index 0000000000000..1492f7da1fcfe --- /dev/null +++ b/packages/kbn-recently-accessed/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-recently-accessed'], +}; diff --git a/packages/kbn-recently-accessed/kibana.jsonc b/packages/kbn-recently-accessed/kibana.jsonc new file mode 100644 index 0000000000000..0ec9917dc6b77 --- /dev/null +++ b/packages/kbn-recently-accessed/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/recently-accessed", + "owner": "@elastic/appex-sharedux" +} diff --git a/packages/kbn-recently-accessed/package.json b/packages/kbn-recently-accessed/package.json new file mode 100644 index 0000000000000..825ccbd8cfdaf --- /dev/null +++ b/packages/kbn-recently-accessed/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/recently-accessed", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/create_log_key.test.ts b/packages/kbn-recently-accessed/src/create_log_key.test.ts similarity index 100% rename from packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/create_log_key.test.ts rename to packages/kbn-recently-accessed/src/create_log_key.test.ts diff --git a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/create_log_key.ts b/packages/kbn-recently-accessed/src/create_log_key.ts similarity index 88% rename from packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/create_log_key.ts rename to packages/kbn-recently-accessed/src/create_log_key.ts index a9b0c5cdcf7d4..5a5e8e7cc7ad0 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/create_log_key.ts +++ b/packages/kbn-recently-accessed/src/create_log_key.ts @@ -8,7 +8,7 @@ import { Sha256 } from '@kbn/crypto-browser'; -export async function createLogKey(type: string, optionalIdentifier?: string) { +export function createLogKey(type: string, optionalIdentifier?: string) { const baseKey = `kibana.history.${type}`; if (!optionalIdentifier) { diff --git a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/index.ts b/packages/kbn-recently-accessed/src/index.ts similarity index 84% rename from packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/index.ts rename to packages/kbn-recently-accessed/src/index.ts index 2256f60061b87..389b7eca5c1b2 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/index.ts +++ b/packages/kbn-recently-accessed/src/index.ts @@ -7,3 +7,4 @@ */ export { RecentlyAccessedService } from './recently_accessed_service'; +export type { RecentlyAccessed, RecentlyAccessedHistoryItem } from './types'; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/persisted_log.test.ts b/packages/kbn-recently-accessed/src/persisted_log.test.ts similarity index 100% rename from packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/persisted_log.test.ts rename to packages/kbn-recently-accessed/src/persisted_log.test.ts diff --git a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/persisted_log.ts b/packages/kbn-recently-accessed/src/persisted_log.ts similarity index 100% rename from packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/persisted_log.ts rename to packages/kbn-recently-accessed/src/persisted_log.ts diff --git a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/recently_accessed_service.test.ts b/packages/kbn-recently-accessed/src/recently_accessed_service.test.ts similarity index 98% rename from packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/recently_accessed_service.test.ts rename to packages/kbn-recently-accessed/src/recently_accessed_service.test.ts index e394f81fab11d..0260949295634 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/recently_accessed_service.test.ts +++ b/packages/kbn-recently-accessed/src/recently_accessed_service.test.ts @@ -54,7 +54,10 @@ describe('RecentlyAccessed#start()', () => { const getStart = async () => { const http = httpServiceMock.createStartContract(); - const recentlyAccessed = await new RecentlyAccessedService().start({ http }); + const recentlyAccessed = await new RecentlyAccessedService().start({ + http, + key: 'recentlyAccessed', + }); return { http, recentlyAccessed }; }; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/recently_accessed_service.ts b/packages/kbn-recently-accessed/src/recently_accessed_service.ts similarity index 68% rename from packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/recently_accessed_service.ts rename to packages/kbn-recently-accessed/src/recently_accessed_service.ts index ec9a04ab5551c..4df97b556e5b6 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/recently_accessed/recently_accessed_service.ts +++ b/packages/kbn-recently-accessed/src/recently_accessed_service.ts @@ -6,23 +6,20 @@ * Side Public License, v 1. */ -import type { InternalHttpStart } from '@kbn/core-http-browser-internal'; -import type { - ChromeRecentlyAccessed, - ChromeRecentlyAccessedHistoryItem, -} from '@kbn/core-chrome-browser'; +import type { HttpStart } from '@kbn/core-http-browser'; +import type { RecentlyAccessed, RecentlyAccessedHistoryItem } from './types'; import { PersistedLog } from './persisted_log'; import { createLogKey } from './create_log_key'; interface StartDeps { - http: InternalHttpStart; + key: string; + http: Pick; } -/** @internal */ export class RecentlyAccessedService { - async start({ http }: StartDeps): Promise { - const logKey = await createLogKey('recentlyAccessed', http.basePath.get()); - const history = new PersistedLog(logKey, { + start({ http, key }: StartDeps): RecentlyAccessed { + const logKey = createLogKey(key, http.basePath.get()); + const history = new PersistedLog(logKey, { maxLength: 20, isEqual: (oldItem, newItem) => oldItem.id === newItem.id, }); diff --git a/packages/kbn-recently-accessed/src/types.ts b/packages/kbn-recently-accessed/src/types.ts new file mode 100644 index 0000000000000..9b729ffc9ed2c --- /dev/null +++ b/packages/kbn-recently-accessed/src/types.ts @@ -0,0 +1,56 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { Observable } from 'rxjs'; + +/** @public */ +export interface RecentlyAccessedHistoryItem { + link: string; + label: string; + id: string; +} + +/** + * {@link RecentlyAccessed | APIs} for recently accessed history. + * @public + */ +export interface RecentlyAccessed { + /** + * Adds a new item to the recently accessed history. + * + * @example + * ```js + * chrome.recentlyAccessed.add('/app/map/1234', 'Map 1234', '1234'); + * ``` + * + * @param link a relative URL to the resource (not including the {@link HttpStart.basePath | `http.basePath`}) + * @param label the label to display in the UI + * @param id a unique string used to de-duplicate the recently accessed list. + */ + add(link: string, label: string, id: string): void; + + /** + * Gets an Array of the current recently accessed history. + * + * @example + * ```js + * recentlyAccessed.get().forEach(console.log); + * ``` + */ + get(): RecentlyAccessedHistoryItem[]; + + /** + * Gets an Observable of the array of recently accessed history. + * + * @example + * ```js + * recentlyAccessed.get$().subscribe(console.log); + * ``` + */ + get$(): Observable; +} diff --git a/packages/kbn-recently-accessed/tsconfig.json b/packages/kbn-recently-accessed/tsconfig.json new file mode 100644 index 0000000000000..f6029007a4376 --- /dev/null +++ b/packages/kbn-recently-accessed/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/crypto-browser", + "@kbn/core-http-browser", + "@kbn/core-http-browser-mocks", + ] +} diff --git a/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx b/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx index e55b61a380bbf..58f0444f8b63f 100644 --- a/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx +++ b/packages/kbn-test-eui-helpers/src/rtl_helpers.tsx @@ -53,7 +53,7 @@ export class EuiButtonGroupTestHarness { } /** - * Returns selected value of button group + * Returns selected option of button group */ public get selected() { return within(this.#buttonGroup).getByRole('button', { pressed: true }); @@ -136,3 +136,59 @@ export class EuiSuperDatePickerTestHarness { userEvent.click(screen.getByRole('button', { name: 'Refresh' })); } } + +export class EuiSelectTestHarness { + #testId: string; + + /** + * Returns select or throws + */ + get #selectEl() { + return screen.getByTestId(this.#testId); + } + + constructor(testId: string) { + this.#testId = testId; + } + + /** + * Returns `data-test-subj` of select + */ + public get testId() { + return this.#testId; + } + + /** + * Returns button select if found, otherwise `null` + */ + public get self() { + return screen.queryByTestId(this.#testId); + } + + /** + * Returns all options of select + */ + public get options(): HTMLOptionElement[] { + return within(this.#selectEl).getAllByRole('option'); + } + + /** + * Returns selected option + */ + public get selected() { + return (this.#selectEl as HTMLSelectElement).value; + } + + /** + * Select option by value + */ + public select(optionName: string | RegExp) { + const option = this.options.find((o) => o.value === optionName)?.value; + + if (!option) { + throw new Error(`Option [${optionName}] not found`); + } + + fireEvent.change(this.#selectEl, { target: { value: option } }); + } +} diff --git a/packages/kbn-ui-shared-deps-npm/BUILD.bazel b/packages/kbn-ui-shared-deps-npm/BUILD.bazel index f2ff64942a993..937cbe0c2a8ef 100644 --- a/packages/kbn-ui-shared-deps-npm/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-npm/BUILD.bazel @@ -47,6 +47,7 @@ RUNTIME_DEPS = [ "@npm//@tanstack/react-query-devtools", "@npm//classnames", "@npm//fflate", + "@npm//fastest-levenshtein", "@npm//history", "@npm//jquery", "@npm//lodash", diff --git a/packages/kbn-ui-shared-deps-npm/webpack.config.js b/packages/kbn-ui-shared-deps-npm/webpack.config.js index 24085109d0d56..294bffdaaa833 100644 --- a/packages/kbn-ui-shared-deps-npm/webpack.config.js +++ b/packages/kbn-ui-shared-deps-npm/webpack.config.js @@ -77,6 +77,7 @@ module.exports = (_, argv) => { '@tanstack/react-query-devtools', 'classnames', 'fflate', + 'fastest-levenshtein', 'history', 'io-ts', 'jquery', diff --git a/packages/kbn-ui-shared-deps-src/BUILD.bazel b/packages/kbn-ui-shared-deps-src/BUILD.bazel index 3d7dd87020047..cd723a8ed6401 100644 --- a/packages/kbn-ui-shared-deps-src/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-src/BUILD.bazel @@ -28,6 +28,7 @@ webpack_cli( "//packages/kbn-monaco", "//packages/kbn-datemath", "//packages/kbn-analytics", + "//packages/kbn-crypto-browser", "//packages/kbn-es-query", "//packages/kbn-search-errors", "//packages/kbn-std", diff --git a/packages/kbn-ui-shared-deps-src/src/definitions.js b/packages/kbn-ui-shared-deps-src/src/definitions.js index b2b38e131fb80..6f7bff397d320 100644 --- a/packages/kbn-ui-shared-deps-src/src/definitions.js +++ b/packages/kbn-ui-shared-deps-src/src/definitions.js @@ -61,6 +61,7 @@ const externals = { redux: '__kbnSharedDeps__.Redux', immer: '__kbnSharedDeps__.Immer', reselect: '__kbnSharedDeps__.Reselect', + 'fastest-levenshtein': '__kbnSharedDeps__.FastestLevenshtein', /** * big deps which are locked to a single version @@ -88,6 +89,7 @@ const externals = { tslib: '__kbnSharedDeps__.TsLib', uuid: '__kbnSharedDeps__.Uuid', '@kbn/analytics': '__kbnSharedDeps__.KbnAnalytics', + '@kbn/crypto-browser': '__kbnSharedDeps__.KbnCryptoBrowser', '@kbn/es-query': '__kbnSharedDeps__.KbnEsQuery', '@kbn/search-errors': '__kbnSharedDeps__.KbnSearchErrors', '@kbn/std': '__kbnSharedDeps__.KbnStd', diff --git a/packages/kbn-ui-shared-deps-src/src/entry.js b/packages/kbn-ui-shared-deps-src/src/entry.js index 1d0e88ef0efd8..5347952978780 100644 --- a/packages/kbn-ui-shared-deps-src/src/entry.js +++ b/packages/kbn-ui-shared-deps-src/src/entry.js @@ -31,6 +31,7 @@ export const ReactRouter = require('react-router'); export const ReactRouterDom = require('react-router-dom'); export const ReactRouterDomV5Compat = require('react-router-dom-v5-compat'); export const StyledComponents = require('styled-components'); +export const FastestLevenshtein = require('fastest-levenshtein'); Moment.tz.load(require('moment-timezone/data/packed/latest.json')); @@ -60,6 +61,7 @@ export const Fflate = { unzlibSync, strFromU8 }; export const TsLib = require('tslib'); export const Uuid = require('uuid'); export const KbnAnalytics = require('@kbn/analytics'); +export const KbnCryptoBrowser = require('@kbn/crypto-browser'); export const KbnEsQuery = require('@kbn/es-query'); export const KbnSearchErrors = require('@kbn/search-errors'); export const KbnStd = require('@kbn/std'); diff --git a/packages/kbn-unified-field-list/src/components/field_list_filters/field_name_search.test.tsx b/packages/kbn-unified-field-list/src/components/field_list_filters/field_name_search.test.tsx index e5a5a4bb82a5f..52858ba292812 100644 --- a/packages/kbn-unified-field-list/src/components/field_list_filters/field_name_search.test.tsx +++ b/packages/kbn-unified-field-list/src/components/field_list_filters/field_name_search.test.tsx @@ -8,12 +8,18 @@ import React, { useState } from 'react'; import userEvent from '@testing-library/user-event'; +import { render, screen } from '@testing-library/react'; import { FieldNameSearch, type FieldNameSearchProps } from './field_name_search'; -import { render, screen, waitFor } from '@testing-library/react'; -// Flaky: https://github.com/elastic/kibana/issues/187714 -describe.skip('UnifiedFieldList ', () => { - it('should render correctly', async () => { +describe('UnifiedFieldList ', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + afterAll(() => { + jest.useRealTimers(); + }); + + it('should render correctly', () => { const props: FieldNameSearchProps = { nameFilter: '', onChange: jest.fn(), @@ -24,11 +30,12 @@ describe.skip('UnifiedFieldList ', () => { const input = screen.getByRole('searchbox', { name: 'Search field names' }); expect(input).toHaveAttribute('aria-describedby', 'htmlId'); userEvent.type(input, 'hey'); - await waitFor(() => expect(props.onChange).toHaveBeenCalledWith('hey'), { timeout: 256 }); + jest.advanceTimersByTime(256); + expect(props.onChange).toHaveBeenCalledWith('hey'); expect(props.onChange).toBeCalledTimes(1); }); - it('should accept the updates from the top', async () => { + it('should accept the updates from the top', () => { const FieldNameSearchWithWrapper = ({ defaultNameFilter = '' }) => { const [nameFilter, setNameFilter] = useState(defaultNameFilter); const props: FieldNameSearchProps = { diff --git a/packages/kbn-unified-field-list/src/hooks/use_field_filters.test.tsx b/packages/kbn-unified-field-list/src/hooks/use_field_filters.test.tsx index 8e86535f0bb85..17d937b270d86 100644 --- a/packages/kbn-unified-field-list/src/hooks/use_field_filters.test.tsx +++ b/packages/kbn-unified-field-list/src/hooks/use_field_filters.test.tsx @@ -63,7 +63,9 @@ describe('UnifiedFieldList useFieldFilters()', () => { expect(result.current.fieldSearchHighlight).toBe('time'); expect(result.current.onFilterField).toBeDefined(); - expect(result.current.onFilterField!({ displayName: 'time test' } as DataViewField)).toBe(true); + expect( + result.current.onFilterField!({ displayName: 'time test', name: '' } as DataViewField) + ).toBe(true); expect(result.current.onFilterField!(dataView.getFieldByName('@timestamp')!)).toBe(true); expect(result.current.onFilterField!(dataView.getFieldByName('bytes')!)).toBe(false); }); @@ -86,14 +88,46 @@ describe('UnifiedFieldList useFieldFilters()', () => { expect(result.current.fieldSearchHighlight).toBe('message*me1'); expect(result.current.onFilterField).toBeDefined(); - expect(result.current.onFilterField!({ displayName: 'test' } as DataViewField)).toBe(false); - expect(result.current.onFilterField!({ displayName: 'message' } as DataViewField)).toBe(false); - expect(result.current.onFilterField!({ displayName: 'message.name1' } as DataViewField)).toBe( - true + expect(result.current.onFilterField!({ displayName: 'test', name: '' } as DataViewField)).toBe( + false ); + expect( + result.current.onFilterField!({ displayName: 'message', name: '' } as DataViewField) + ).toBe(false); + expect( + result.current.onFilterField!({ displayName: 'message.name1', name: '' } as DataViewField) + ).toBe(true); expect(result.current.onFilterField!({ name: 'messagename10' } as DataViewField)).toBe(false); expect(result.current.onFilterField!({ name: 'message.test' } as DataViewField)).toBe(false); }); + it('should update correctly on search by name with a typo', async () => { + const props: FieldFiltersParams = { + allFields: dataView.fields, + services: mockedServices, + }; + const { result } = renderHook(useFieldFilters, { + initialProps: props, + }); + + expect(result.current.fieldSearchHighlight).toBe(''); + expect(result.current.onFilterField).toBeUndefined(); + + act(() => { + result.current.fieldListFiltersProps.onChangeNameFilter('messsge'); + }); + + expect(result.current.fieldSearchHighlight).toBe('messsge'); + expect(result.current.onFilterField).toBeDefined(); + expect(result.current.onFilterField!({ displayName: 'test', name: '' } as DataViewField)).toBe( + false + ); + expect( + result.current.onFilterField!({ displayName: 'message', name: '' } as DataViewField) + ).toBe(true); + expect( + result.current.onFilterField!({ displayName: 'message.name1', name: '' } as DataViewField) + ).toBe(true); + }); it('should update correctly on filter by type', async () => { const props: FieldFiltersParams = { diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 73f8b7f9e9313..51c06947d224b 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -88,7 +88,7 @@ describe('checking migration metadata changes on all registered SO types', () => "endpoint:unified-user-artifact-manifest": "71c7fcb52c658b21ea2800a6b6a76972ae1c776e", "endpoint:user-artifact-manifest": "1c3533161811a58772e30cdc77bac4631da3ef2b", "enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d", - "entity-definition": "33fe0194bd896f0bfe479d55f6de20f8ba1d7713", + "entity-definition": "331a2ba0ee9f24936ef049683549c8af7e46f03a", "entity-discovery-api-key": "c267a65c69171d1804362155c1378365f5acef88", "epm-packages": "8042d4a1522f6c4e6f5486e791b3ffe3a22f88fd", "epm-packages-assets": "7a3e58efd9a14191d0d1a00b8aaed30a145fd0b1", diff --git a/src/core/server/integration_tests/elasticsearch/user_agent.test.ts b/src/core/server/integration_tests/elasticsearch/user_agent.test.ts new file mode 100644 index 0000000000000..b864e3f330308 --- /dev/null +++ b/src/core/server/integration_tests/elasticsearch/user_agent.test.ts @@ -0,0 +1,76 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { esTestConfig } from '@kbn/test'; +import * as http from 'http'; +import { loggerMock } from '@kbn/logging-mocks'; +import { Root } from '@kbn/core-root-server-internal'; +import { + PRODUCT_RESPONSE_HEADER, + USER_AGENT_HEADER, + configureClient, + AgentManager, +} from '@kbn/core-elasticsearch-client-server-internal'; +import { configSchema, ElasticsearchConfig } from '@kbn/core-elasticsearch-server-internal'; + +function createFakeElasticsearchServer(hook: (req: http.IncomingMessage) => void) { + const server = http.createServer((req, res) => { + hook(req); + res.writeHead(200, undefined, { [PRODUCT_RESPONSE_HEADER]: 'Elasticsearch' }); + res.write('{}'); + res.end(); + }); + server.listen(esTestConfig.getPort()); + + return server; +} + +describe('ES Client - custom user-agent', () => { + let esServer: http.Server; + let kibanaServer: Root; + + afterAll(async () => { + try { + await kibanaServer?.shutdown(); + } catch (e) { + // trap + } + try { + await new Promise((resolve, reject) => + esServer.close((err) => (err ? reject(err) : resolve())) + ); + } catch (e) { + // trap + } + }); + + test('should send a custom user-agent header matching the expected format', async () => { + const kibanaVersion = '8.42.9'; + const logger = loggerMock.create(); + const rawConfig = configSchema.validate({ + hosts: [`${esTestConfig.getUrl()}`], + }); + const config = new ElasticsearchConfig(rawConfig); + const agentFactoryProvider = new AgentManager(logger, { dnsCacheTtlInSeconds: 0 }); + const esClient = configureClient(config, { + type: 'foo', + logger, + kibanaVersion, + agentFactoryProvider, + }); + + let userAgentHeader: string | undefined; + esServer = createFakeElasticsearchServer((res) => { + userAgentHeader = res.headers[USER_AGENT_HEADER]; + }); + + await esClient.ping(); + + expect(userAgentHeader).toEqual(`Kibana/${kibanaVersion}`); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_failed_action_tasks.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_failed_action_tasks.test.ts index 89478ea377f6c..16e0043062a63 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_failed_action_tasks.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_failed_action_tasks.test.ts @@ -57,8 +57,6 @@ describe('migration from 7.13 to 7.14+ with many failed action_tasks', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); const getCounts = async ( diff --git a/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_unknown_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_unknown_types.test.ts index 6b06337a367db..d9c4f0d7f33ae 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_unknown_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group1/7_13_0_unknown_types.test.ts @@ -58,8 +58,6 @@ describe('migration v2', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); describe('when `discardUnknownObjects` does not match current kibana version', () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts index bea50cf32ce35..e6c3ad412e746 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes.test.ts @@ -89,8 +89,6 @@ describe('migration v2', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('completes the migration even when a full batch would exceed ES http.max_content_length', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes_exceeds_es_content_length.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes_exceeds_es_content_length.test.ts index 95baed313bd8c..4cdc89006c983 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes_exceeds_es_content_length.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/batch_size_bytes_exceeds_es_content_length.test.ts @@ -58,8 +58,6 @@ describe('migration v2', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('fails with a descriptive message when maxBatchSizeBytes exceeds ES http.max_content_length', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_target_mappings.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_target_mappings.test.ts index e79084ebdc34a..600a00e24e9fd 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_target_mappings.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_target_mappings.test.ts @@ -17,7 +17,6 @@ import { createTestServers, type TestElasticsearchUtils, } from '@kbn/core-test-helpers-kbn-server'; -import { delay } from '../test_utils'; const logFilePath = Path.join(__dirname, 'check_target_mappings.log'); @@ -33,7 +32,6 @@ describe('migration v2 - CHECK_TARGET_MAPPINGS', () => { afterEach(async () => { await root?.shutdown(); await esServer?.stop(); - await delay(10); }); it('is not run for new installations', async () => { @@ -80,7 +78,6 @@ describe('migration v2 - CHECK_TARGET_MAPPINGS', () => { // stop Kibana and remove logs await root.shutdown(); - await delay(10); await fs.unlink(logFilePath).catch(() => {}); root = createRoot(); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts index 7f04f37589f69..7c93c1477cf4a 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts @@ -13,7 +13,7 @@ import JSON5 from 'json5'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { SavedObjectsType } from '@kbn/core-saved-objects-server'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { getMigrationDocLink, delay } from '../test_utils'; +import { getMigrationDocLink } from '../test_utils'; import { clearLog, currentVersion, @@ -71,7 +71,6 @@ describe('migration v2', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/collects_corrupt_docs.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/collects_corrupt_docs.test.ts index 45b28e8ac3082..dbc5ea193ad56 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/collects_corrupt_docs.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/collects_corrupt_docs.test.ts @@ -42,8 +42,6 @@ describe('migration v2 with corrupt saved object documents', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('collects corrupt saved object documents across batches', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/corrupt_outdated_docs.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/corrupt_outdated_docs.test.ts index 384cdcbfa8abd..728625003637e 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/corrupt_outdated_docs.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/corrupt_outdated_docs.test.ts @@ -40,8 +40,6 @@ describe('migration v2 with corrupt saved object documents', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it.skip('collects corrupt saved object documents across batches', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/multiple_kibana_nodes.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/multiple_kibana_nodes.test.ts index 80d75d757ca4d..d82863f01928c 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/multiple_kibana_nodes.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/multiple_kibana_nodes.test.ts @@ -171,7 +171,6 @@ describe.skip('migration v2', () => { if (esServer) { await esServer.stop(); - await delay(10000); } }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/outdated_docs.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/outdated_docs.test.ts index 79a76c72adefc..e7579a7ce4a91 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/outdated_docs.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/outdated_docs.test.ts @@ -41,8 +41,6 @@ describe('migration v2', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('migrates the documents to the highest version', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/deferred_migrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/deferred_migrations.test.ts index 8a40466ee2588..41c9501d7e6b2 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/deferred_migrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/deferred_migrations.test.ts @@ -12,7 +12,6 @@ import type { SavedObjectsType, SavedObjectUnsanitizedDoc, } from '@kbn/core-saved-objects-server'; -import { delay } from '../test_utils'; import '../jest_matchers'; import { @@ -36,7 +35,6 @@ describe('deferred migrations', () => { afterAll(async () => { await server?.stop(); - await delay(10); }); beforeEach(async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts index d32c2f0902b4e..45ffbb709854c 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts @@ -170,8 +170,6 @@ describe('migrating from 7.3.0-xpack which used v1 migrations', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }; beforeAll(async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts index e4350c431977b..73e31881b0a91 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/multiple_es_nodes.test.ts @@ -103,10 +103,6 @@ describe('migration v2', () => { await removeLogFile(); }); - afterAll(async () => { - await new Promise((resolve) => setTimeout(resolve, 10000)); - }); - afterEach(async () => { if (root) { await root.shutdown(); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts index 0fce643975c53..3a91a8477d4e5 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/read_batch_size.test.ts @@ -13,7 +13,6 @@ import { createRootWithCorePlugins, type TestElasticsearchUtils, } from '@kbn/core-test-helpers-kbn-server'; -import { delay } from '../test_utils'; import { startElasticsearch } from '../kibana_migrator_test_kit'; const logFilePath = Path.join(__dirname, 'read_batch_size.log'); @@ -33,7 +32,6 @@ describe('migration v2 - read batch size', () => { afterEach(async () => { await root?.shutdown(); await esServer?.stop(); - await delay(10); }); it('reduces the read batchSize in half if a batch exceeds maxReadBatchSizeBytes', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts index 6079469cf4982..70768bd3f7009 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/rewriting_id.test.ts @@ -108,8 +108,6 @@ describe('migration v2', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('rewrites id deterministically for SO with namespaceType: "multiple" and "multiple-isolated"', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/skip_migration.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/skip_migration.test.ts index e24c50d2cb4c9..54ac46754acc4 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/skip_migration.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/skip_migration.test.ts @@ -71,8 +71,6 @@ describe('starting with `migration.skip: true` when indices are up to date', () if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('starts and display the correct service status', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/wait_for_migration_completion.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/wait_for_migration_completion.test.ts index 459cfc921badf..266e821abf7e3 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/wait_for_migration_completion.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/wait_for_migration_completion.test.ts @@ -40,8 +40,6 @@ describe('migration with waitForCompletion=true', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('waits for another instance to complete the migration', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group4/v2_md5_to_mv.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group4/v2_md5_to_mv.test.ts index 4e1cb4d06e38f..1277370f45835 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group4/v2_md5_to_mv.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group4/v2_md5_to_mv.test.ts @@ -19,7 +19,7 @@ import { readLog, startElasticsearch, } from '../kibana_migrator_test_kit'; -import { delay, createType } from '../test_utils'; +import { createType } from '../test_utils'; import '../jest_matchers'; const logFilePath = Path.join(__dirname, 'v2_md5_to_mv.test.log'); @@ -270,6 +270,5 @@ describe('V2 algorithm', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_same_stack_version.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_same_stack_version.test.ts index bfc105ee7d160..ba4a19de81741 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_same_stack_version.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_same_stack_version.test.ts @@ -14,7 +14,7 @@ import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server import { modelVersionToVirtualVersion } from '@kbn/core-saved-objects-base-server-internal'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, createType, parseLogFile } from '../test_utils'; +import { createType, parseLogFile } from '../test_utils'; import { getBaseMigratorParams } from '../fixtures/zdt_base.fixtures'; const logFilePath = Path.join(__dirname, 'v2_with_mv_same_stack_version.test.log'); @@ -31,7 +31,6 @@ describe('V2 algorithm - using model versions - upgrade without stack version in afterAll(async () => { await esServer?.stop(); - await delay(10); }); const getTestModelVersionType = ({ beforeUpgrade }: { beforeUpgrade: boolean }) => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_stack_version_bump.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_stack_version_bump.test.ts index 7a5d8a686f084..f24dc6fd427fe 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_stack_version_bump.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group4/v2_with_mv_stack_version_bump.test.ts @@ -14,7 +14,7 @@ import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server import { modelVersionToVirtualVersion } from '@kbn/core-saved-objects-base-server-internal'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, createType, parseLogFile } from '../test_utils'; +import { createType, parseLogFile } from '../test_utils'; import { getBaseMigratorParams } from '../fixtures/zdt_base.fixtures'; const logFilePath = Path.join(__dirname, 'v2_with_mv_stack_version_bump.test.log'); @@ -31,7 +31,6 @@ describe('V2 algorithm - using model versions - stack version bump scenario', () afterAll(async () => { await esServer?.stop(); - await delay(10); }); const getTestSwitchType = ({ beforeUpgrade }: { beforeUpgrade: boolean }) => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete.test.ts index d950129dac69a..cd1a98fa3ee57 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete.test.ts @@ -22,7 +22,6 @@ import { getIncompatibleMappingsMigrator, getNonDeprecatedMappingsMigrator, } from '../kibana_migrator_test_kit'; -import { delay } from '../test_utils'; describe('when upgrading to a new stack version', () => { let esServer: TestElasticsearchUtils['es']; @@ -34,7 +33,6 @@ describe('when upgrading to a new stack version', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); describe('if the mappings match (diffMappings() === false)', () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete_multiple_instances.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete_multiple_instances.test.ts index e297b39847f10..e0233cccb3fb7 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete_multiple_instances.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group5/active_delete_multiple_instances.test.ts @@ -23,7 +23,6 @@ import { startElasticsearch, } from '../kibana_migrator_test_kit'; import { baselineTypes } from './active_delete.fixtures'; -import { delay } from '../test_utils'; import { createBaselineArchive } from '../kibana_migrator_archive_utils'; const PARALLEL_MIGRATORS = 6; @@ -146,7 +145,6 @@ describe('multiple migrator instances running in parallel', () => { afterAll(async () => { // await esClient?.indices.delete({ index: `${kibanaIndex}_${currentVersion}_001` }); await esServer?.stop(); - await delay(10); }); const getAggregatedTypesCount = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group5/skip_reindex.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group5/skip_reindex.test.ts index 4d244314d3acb..b93f96671d96f 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group5/skip_reindex.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group5/skip_reindex.test.ts @@ -20,7 +20,6 @@ import { startElasticsearch, KibanaMigratorTestKit, } from '../kibana_migrator_test_kit'; -import { delay } from '../test_utils'; describe('when migrating to a new version', () => { let esServer: TestElasticsearchUtils['es']; @@ -144,6 +143,5 @@ describe('when migrating to a new version', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/basic_document_migration.ts b/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/basic_document_migration.ts index 0c43a40478d03..9e3d01dd0ecec 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/basic_document_migration.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/basic_document_migration.ts @@ -11,7 +11,7 @@ import { range, sortBy } from 'lodash'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import '../../jest_matchers'; import { getKibanaMigratorTestKit } from '../../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../../test_utils'; +import { parseLogFile } from '../../test_utils'; import { EsRunner, EsServer } from '../../test_types'; import { getBaseMigratorParams, @@ -35,7 +35,6 @@ export function createBasicDocumentsMigrationTest({ afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/standard_workflow.ts b/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/standard_workflow.ts index b22e522d1d2c1..f0709e8720ce7 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/standard_workflow.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/shared_suites/zdt/standard_workflow.ts @@ -11,7 +11,7 @@ import { range } from 'lodash'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import '../../jest_matchers'; import { getKibanaMigratorTestKit } from '../../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../../test_utils'; +import { parseLogFile } from '../../test_utils'; import { EsRunner, EsServer } from '../../test_types'; import { getBaseMigratorParams, @@ -36,7 +36,6 @@ export function createStandardWorkflowTest({ afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/basic_downgrade.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/basic_downgrade.test.ts index 8968fe7bac408..c541f57a6111a 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/basic_downgrade.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/basic_downgrade.test.ts @@ -13,7 +13,7 @@ import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile, createType } from '../test_utils'; +import { parseLogFile, createType } from '../test_utils'; import { getBaseMigratorParams } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'basic_downgrade.test.log'); @@ -28,7 +28,6 @@ describe('ZDT upgrades - basic downgrade', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const typeV1 = createType({ diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/conversion_failures.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/conversion_failures.test.ts index 05f8f30edacc6..b9821dc62442b 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/conversion_failures.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/conversion_failures.test.ts @@ -13,7 +13,7 @@ import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getSampleAType, @@ -31,7 +31,6 @@ describe('ZDT upgrades - encountering conversion failures', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); beforeEach(async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/create_index.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/create_index.test.ts index f0b049b8307db..6f12311009e21 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/create_index.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/create_index.test.ts @@ -11,7 +11,7 @@ import fs from 'fs/promises'; import '../jest_matchers'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType, getBarType } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'create_index.test.log'); @@ -26,7 +26,6 @@ describe('ZDT upgrades - running on a fresh cluster', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); it('create the index with the correct mappings and meta', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/document_cleanup.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/document_cleanup.test.ts index ab707f792fbe9..e0341915c7dd3 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/document_cleanup.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/document_cleanup.test.ts @@ -13,7 +13,6 @@ import { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay } from '../test_utils'; import { getBaseMigratorParams, getDeletedType, @@ -35,7 +34,6 @@ describe('ZDT upgrades - document cleanup', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/mapping_version_conflict.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/mapping_version_conflict.test.ts index 7b0658fbd977e..a19db9811462c 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/mapping_version_conflict.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/mapping_version_conflict.test.ts @@ -11,7 +11,7 @@ import fs from 'fs/promises'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType, @@ -33,7 +33,6 @@ describe('ZDT upgrades - mapping model version conflict', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/rerun_same_version.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/rerun_same_version.test.ts index 398a133bf990a..6c4d8688450bf 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/rerun_same_version.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/rerun_same_version.test.ts @@ -11,7 +11,7 @@ import fs from 'fs/promises'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType, getBarType } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'rerun_same_version.test.log'); @@ -26,7 +26,6 @@ describe('ZDT upgrades - rerun migration on same version', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/update_mappings.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/update_mappings.test.ts index c51c712f34452..82ff0a1c0d5ec 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_1/update_mappings.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/update_mappings.test.ts @@ -11,7 +11,7 @@ import fs from 'fs/promises'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType, @@ -31,7 +31,6 @@ describe('ZDT upgrades - basic mapping update', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/outdated_doc_query.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/outdated_doc_query.test.ts index cc901b56f3818..f403fe6c3579a 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/outdated_doc_query.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/outdated_doc_query.test.ts @@ -12,7 +12,7 @@ import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import '../jest_matchers'; import { SavedObjectsModelVersionMap, SavedObject } from '@kbn/core-saved-objects-server'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, createType } from '../test_utils'; +import { createType } from '../test_utils'; import { getBaseMigratorParams } from '../fixtures/zdt_base.fixtures'; import { SavedObjectsSerializer, @@ -114,7 +114,6 @@ describe('getOutdatedDocumentsQuery', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); it('creates a query returning the expected documents', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/root_field_addition.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/root_field_addition.test.ts index 47f853187b18f..1ce530a9c46c1 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/root_field_addition.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/root_field_addition.test.ts @@ -27,7 +27,7 @@ import fs from 'fs/promises'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'root_field_addition.test.log'); @@ -42,7 +42,6 @@ describe('ZDT upgrades - introducing new root fields', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const baseMappings = { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/type_addition.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/type_addition.test.ts index 61697df6ecef6..770fade1d436c 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/type_addition.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/type_addition.test.ts @@ -11,7 +11,7 @@ import fs from 'fs/promises'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType, getBarType } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'type_addition.test.log'); @@ -26,7 +26,6 @@ describe('ZDT upgrades - introducing a new SO type', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_partial_failure.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_partial_failure.test.ts index 0cfebd3d8514b..fb3c11e68299f 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_partial_failure.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_partial_failure.test.ts @@ -13,7 +13,7 @@ import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile, createType } from '../test_utils'; +import { parseLogFile, createType } from '../test_utils'; import { getBaseMigratorParams, noopMigration } from '../fixtures/zdt_base.fixtures'; const logFilePath = Path.join(__dirname, 'v2_to_zdt_partial_failure.test.log'); @@ -28,7 +28,6 @@ describe('ZDT with v2 compat - recovering from partially migrated state', () => afterAll(async () => { await esServer?.stop(); - await delay(10); }); const typeBefore = createType({ diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_switch.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_switch.test.ts index 4a92a9d14eef4..dfbe0aeba8866 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_switch.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_2/v2_to_zdt_switch.test.ts @@ -18,7 +18,7 @@ import { startElasticsearch, currentVersion, } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getSampleAType } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'v2_to_zdt_switch.test.log'); @@ -35,7 +35,6 @@ describe('ZDT upgrades - switching from v2 algorithm', () => { afterEach(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async ({ diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/basic_document_migration.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/basic_document_migration.test.ts index c71ca527f1270..5bd16f0110c63 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/basic_document_migration.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/basic_document_migration.test.ts @@ -13,7 +13,7 @@ import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getSampleAType, @@ -32,7 +32,6 @@ describe('ZDT with v2 compat - basic document migration', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/create_index.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/create_index.test.ts index 6f1b8257ddb26..d9f76907087be 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/create_index.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/create_index.test.ts @@ -11,7 +11,7 @@ import fs from 'fs/promises'; import '../jest_matchers'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType, getLegacyType } from '../fixtures/zdt_base.fixtures'; const logFilePath = Path.join(__dirname, 'create_index.test.log'); @@ -26,7 +26,6 @@ describe('ZDT with v2 compat - running on a fresh cluster', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); it('create the index with the correct mappings and meta', async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/switch_to_model_version.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/switch_to_model_version.test.ts index 1aa65bc1cdd02..fa72c8f1edae8 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/switch_to_model_version.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/switch_to_model_version.test.ts @@ -13,7 +13,7 @@ import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile, createType } from '../test_utils'; +import { parseLogFile, createType } from '../test_utils'; import { getBaseMigratorParams, noopMigration } from '../fixtures/zdt_base.fixtures'; const logFilePath = Path.join(__dirname, 'switch_to_model_version.test.log'); @@ -28,7 +28,6 @@ describe('ZDT with v2 compat - type switching from migration to model version', afterAll(async () => { await esServer?.stop(); - await delay(10); }); const typeBefore = createType({ diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/update_mappings.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/update_mappings.test.ts index 0c4956105fc78..9f3e58d6812fa 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/update_mappings.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/update_mappings.test.ts @@ -11,7 +11,7 @@ import fs from 'fs/promises'; import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import '../jest_matchers'; import { getKibanaMigratorTestKit, startElasticsearch } from '../kibana_migrator_test_kit'; -import { delay, parseLogFile } from '../test_utils'; +import { parseLogFile } from '../test_utils'; import { getBaseMigratorParams, getFooType, @@ -32,7 +32,6 @@ describe('ZDT with v2 compat - basic mapping update', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const createBaseline = async () => { diff --git a/src/core/server/integration_tests/saved_objects/service/lib/bulk_update.test.ts b/src/core/server/integration_tests/saved_objects/service/lib/bulk_update.test.ts index 98af19ec5c9d0..4cd661add5053 100644 --- a/src/core/server/integration_tests/saved_objects/service/lib/bulk_update.test.ts +++ b/src/core/server/integration_tests/saved_objects/service/lib/bulk_update.test.ts @@ -17,7 +17,6 @@ import { getKibanaMigratorTestKit, startElasticsearch, } from '../../migrations/kibana_migrator_test_kit'; -import { delay } from '../../migrations/test_utils'; import { getBaseMigratorParams } from '../../migrations/fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'bulk_update.test.log'); @@ -32,7 +31,6 @@ describe('SOR - bulk_update API', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const getCrossVersionType = (version: 'v1' | 'v2'): SavedObjectsType => { diff --git a/src/core/server/integration_tests/saved_objects/service/lib/update.test.ts b/src/core/server/integration_tests/saved_objects/service/lib/update.test.ts index f9205086ceb2e..b4c9c912f7331 100644 --- a/src/core/server/integration_tests/saved_objects/service/lib/update.test.ts +++ b/src/core/server/integration_tests/saved_objects/service/lib/update.test.ts @@ -17,7 +17,6 @@ import { getKibanaMigratorTestKit, startElasticsearch, } from '../../migrations/kibana_migrator_test_kit'; -import { delay } from '../../migrations/test_utils'; import { getBaseMigratorParams } from '../../migrations/fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'update.test.log'); @@ -93,7 +92,6 @@ describe('SOR - update API', () => { afterAll(async () => { await esServer?.stop(); - await delay(10); }); const setup = async () => { diff --git a/src/core/server/integration_tests/saved_objects/validation/validator.test.ts b/src/core/server/integration_tests/saved_objects/validation/validator.test.ts index a0dc22440f1df..4f79a8618df02 100644 --- a/src/core/server/integration_tests/saved_objects/validation/validator.test.ts +++ b/src/core/server/integration_tests/saved_objects/validation/validator.test.ts @@ -164,8 +164,6 @@ describe.skip('validates saved object types when a schema is provided', () => { if (esServer) { await esServer.stop(); } - - await new Promise((resolve) => setTimeout(resolve, 10000)); }); it('does nothing when no schema is provided', async () => { diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index ecb93cfd50792..a8daba08d37b4 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -12,7 +12,7 @@ FROM {{{baseImageName}}} AS builder {{#ubi}} -RUN microdnf install -y findutils tar gzip +RUN microdnf install -y findutils tar gzip{{#fips}} perl make gcc{{/fips}} {{/ubi}} {{#ubuntu}} RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y curl @@ -37,6 +37,30 @@ WORKDIR /usr/share/kibana RUN tar \ --strip-components=1 \ -zxf /tmp/kibana.tar.gz + +{{#fips}} +# OpenSSL requires specific versions that are FIPS certified. +# +# See: +# https://github.com/openssl/openssl/blob/openssl-3.0/README-FIPS.md +# https://www.openssl.org/docs/man3.0/man7/fips_module.html +RUN set -e ; \ + OPENSSL_VERSION='3.0.8'; \ + OPENSSL_PATH=/usr/share/kibana/openssl ; \ + mkdir "${OPENSSL_PATH}"; \ + curl --retry 8 -S -L -O "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" ; \ + curl --retry 8 -S -L -O "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz.sha256" ; \ + echo "$(cat openssl-${OPENSSL_VERSION}.tar.gz.sha256) openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c ; \ + tar -zxf "openssl-${OPENSSL_VERSION}.tar.gz" ; \ + rm -rf openssl-${OPENSSL_VERSION}.tar* ; \ + cd "/usr/share/kibana/openssl-${OPENSSL_VERSION}" ; \ + ./Configure --prefix="${OPENSSL_PATH}" --openssldir="${OPENSSL_PATH}/ssl" --libdir="${OPENSSL_PATH}/lib" enable-fips; \ + make -j $(nproc) > /dev/null ; \ + make install > /dev/null ; \ + rm -rf "/usr/share/kibana/openssl-${OPENSSL_VERSION}" ; \ + chown -R 1000:0 "${OPENSSL_PATH}"; + +{{/fips}} # Ensure that group permissions are the same as user permissions. # This will help when relying on GID-0 to run Kibana, rather than UID-1000. # OpenShift does this, for example. @@ -90,7 +114,7 @@ EXPOSE 5601 RUN for iter in {1..10}; do \ microdnf update --setopt=tsflags=nodocs -y && \ microdnf install --setopt=tsflags=nodocs -y \ - fontconfig freetype shadow-utils nss findutils {{#fips}}perl make gcc tar {{/fips}}&& \ + fontconfig freetype shadow-utils nss findutils && \ microdnf clean all && exit_code=0 && break || exit_code=$? && echo "microdnf error: retry $iter in 10s" && \ sleep 10; \ done; \ @@ -127,30 +151,6 @@ RUN fc-cache -v WORKDIR /usr/share/kibana {{#fips}} -# OpenSSL requires specific versions that are FIPS certified. Further, the FIPS modules -# need to be compiled on the machine to pass its own self validation on startup. -# -# See: -# https://github.com/openssl/openssl/blob/openssl-3.0/README-FIPS.md -# https://www.openssl.org/docs/man3.0/man7/fips_module.html - -# Ideally we would handle this in the builder step, but OpenSSL requires linking of many submodules. -RUN set -e ; \ - OPENSSL_VERSION='3.0.8'; \ - OPENSSL_PATH=/usr/share/kibana/openssl ; \ - mkdir "${OPENSSL_PATH}"; \ - curl --retry 8 -S -L -O "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" ; \ - curl --retry 8 -S -L -O "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz.sha256" ; \ - echo "$(cat openssl-${OPENSSL_VERSION}.tar.gz.sha256) openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c ; \ - tar -zxf "openssl-${OPENSSL_VERSION}.tar.gz" ; \ - rm -rf openssl-${OPENSSL_VERSION}.tar* ; \ - cd "/usr/share/kibana/openssl-${OPENSSL_VERSION}" ; \ - ./Configure --prefix="${OPENSSL_PATH}" --openssldir="${OPENSSL_PATH}/ssl" --libdir="${OPENSSL_PATH}/lib" enable-fips; \ - make -j $(nproc) > /dev/null ; \ - make install > /dev/null ; \ - rm -rf "/usr/share/kibana/openssl-${OPENSSL_VERSION}" ; \ - chown -R 1000:0 "${OPENSSL_PATH}"; - # Enable FIPS for Kibana only. In the future we can override OS wide with ENV OPENSSL_CONF RUN /usr/bin/echo -e '\n--enable-fips' >> config/node.options RUN /usr/bin/echo '--openssl-config=/usr/share/kibana/config/nodejs.cnf' >> config/node.options diff --git a/src/plugins/bfetch/server/ui_settings.ts b/src/plugins/bfetch/server/ui_settings.ts index 697864bec1c59..0dede79980d93 100644 --- a/src/plugins/bfetch/server/ui_settings.ts +++ b/src/plugins/bfetch/server/ui_settings.ts @@ -23,6 +23,12 @@ export function getUiSettings(): Record> { 'Disables requests batching. This increases number of HTTP requests from Kibana, but allows to debug requests individually.', }), schema: schema.boolean(), + deprecation: { + message: i18n.translate('bfetch.advancedSettings.disableBfetchDeprecation', { + defaultMessage: 'This setting is deprecated and will be removed in Kibana 9.0.', + }), + docLinksKey: 'generalSettings', + }, category: [], requiresPageReload: true, }, @@ -36,6 +42,12 @@ export function getUiSettings(): Record> { 'Disable batch compression. This allows you to debug individual requests, but increases response size.', }), schema: schema.boolean(), + deprecation: { + message: i18n.translate('bfetch.advancedSettings.disableBfetchCompressionDeprecation', { + defaultMessage: 'This setting is deprecated and will be removed in Kibana 9.0.', + }), + docLinksKey: 'generalSettings', + }, category: [], requiresPageReload: true, }, diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts index abfa37dd8df6e..88568b1c231cb 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts @@ -25,6 +25,10 @@ describe('interpreter/functions#metricVis', () => { progressDirection: 'horizontal', maxCols: 1, inspectorTableId: 'random-id', + titlesTextAlign: 'left', + valuesTextAlign: 'right', + iconAlign: 'left', + valueFontSize: 'default', }; it('should pass over overrides from variables', async () => { diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts index db1e0cf5cea5f..c40af033ceab9 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts @@ -77,6 +77,30 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ 'The direction the progress bar should grow. Must be provided to render a progress bar.', }), }, + titlesTextAlign: { + types: ['string'], + help: i18n.translate('expressionMetricVis.function.titlesTextAlign.help', { + defaultMessage: 'The alignment of the Title and Subtitle.', + }), + }, + valuesTextAlign: { + types: ['string'], + help: i18n.translate('expressionMetricVis.function.valuesTextAlign.help', { + defaultMessage: 'The alignment of the Primary and Secondary Metric.', + }), + }, + iconAlign: { + types: ['string'], + help: i18n.translate('expressionMetricVis.function.iconAlign.help', { + defaultMessage: 'The alignment of icon.', + }), + }, + valueFontSize: { + types: ['string', 'number'], + help: i18n.translate('expressionMetricVis.function.valueFontSize.help', { + defaultMessage: 'The value font size.', + }), + }, color: { types: ['string'], help: i18n.translate('expressionMetricVis.function.color.help', { @@ -189,6 +213,10 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ icon: args.icon, palette: args.palette?.params, progressDirection: args.progressDirection, + titlesTextAlign: args.titlesTextAlign, + valuesTextAlign: args.valuesTextAlign, + iconAlign: args.iconAlign, + valueFontSize: args.valueFontSize, maxCols: args.maxCols, minTiles: args.minTiles, trends: args.trendline?.trends, diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts index 28199c684ea15..7e7438aa3d8ad 100644 --- a/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts @@ -7,7 +7,7 @@ */ import type { PaletteOutput } from '@kbn/coloring'; -import { LayoutDirection, MetricWTrend } from '@elastic/charts'; +import { LayoutDirection, MetricStyle, MetricWTrend } from '@elastic/charts'; import { $Values } from '@kbn/utility-types'; import { Datatable, @@ -38,6 +38,10 @@ export interface MetricArguments { subtitle?: string; secondaryPrefix?: string; progressDirection?: LayoutDirection; + titlesTextAlign: MetricStyle['titlesTextAlign']; + valuesTextAlign: MetricStyle['valuesTextAlign']; + iconAlign: MetricStyle['iconAlign']; + valueFontSize: MetricStyle['valueFontSize']; color?: string; icon?: string; palette?: PaletteOutput; diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts index b9a43af0752b2..083f464670d89 100644 --- a/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts @@ -8,7 +8,7 @@ import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { CustomPaletteState } from '@kbn/charts-plugin/common'; -import { LayoutDirection } from '@elastic/charts'; +import { LayoutDirection, MetricStyle } from '@elastic/charts'; import { TrendlineResult } from './expression_functions'; export const visType = 'metric'; @@ -27,6 +27,10 @@ export interface MetricVisParam { icon?: string; palette?: CustomPaletteState; progressDirection?: LayoutDirection; + titlesTextAlign: MetricStyle['titlesTextAlign']; + valuesTextAlign: MetricStyle['valuesTextAlign']; + iconAlign: MetricStyle['iconAlign']; + valueFontSize: MetricStyle['valueFontSize']; maxCols: number; minTiles?: number; trends?: TrendlineResult['trends']; diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx index ed57f38ec886f..3687113d9aee0 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx @@ -23,7 +23,7 @@ import { SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { SerializableRecord } from '@kbn/utility-types'; import type { IUiSettingsClient } from '@kbn/core/public'; import { CustomPaletteState } from '@kbn/charts-plugin/common/expressions/palette/types'; -import { DimensionsVisParam } from '../../common'; +import { DimensionsVisParam, MetricVisParam } from '../../common'; import { euiThemeVars } from '@kbn/ui-theme'; import { DEFAULT_TRENDLINE_NAME } from '../../common/constants'; import faker from 'faker'; @@ -73,6 +73,15 @@ const dayOfWeekColumnId = 'col-0-0'; const basePriceColumnId = 'col-1-1'; const minPriceColumnId = 'col-2-2'; +const defaultMetricParams: MetricVisParam = { + progressDirection: 'vertical', + maxCols: 5, + titlesTextAlign: 'left', + valuesTextAlign: 'right', + iconAlign: 'left', + valueFontSize: 'default', +}; + const table: Datatable = { type: 'datatable', columns: [ @@ -217,8 +226,7 @@ describe('MetricVisComponent', function () { describe('single metric', () => { const config: Props['config'] = { metric: { - progressDirection: 'vertical', - maxCols: 5, + ...defaultMetricParams, icon: 'empty', }, dimensions: { @@ -397,13 +405,67 @@ describe('MetricVisComponent', function () { expect(tileConfig.trend).toEqual(trends[DEFAULT_TRENDLINE_NAME]); expect(tileConfig.trendShape).toEqual('area'); }); + + it('should display multi-values non-numeric values formatted and without quotes', () => { + const newTable: Datatable = { + ...table, + // change the format id for the columns + columns: table.columns.map((column) => + [basePriceColumnId, minPriceColumnId].includes(column.id) + ? { + ...column, + meta: { ...column.meta, params: { id: 'text' } }, + } + : column + ), + rows: table.rows.map((row) => ({ + ...row, + [basePriceColumnId]: [String(row[basePriceColumnId]), String(100)], + [minPriceColumnId]: [String(row[minPriceColumnId]), String(10)], + })), + }; + const component = shallow(); + + const [[visConfig]] = component.find(Metric).props().data!; + + expect(visConfig!.value).toMatchInlineSnapshot( + ` + Array [ + "text-28.984375", + "text-100", + ] + ` + ); + }); + + it('should display multi-values numeric values formatted and without quotes', () => { + const newTable = { + ...table, + rows: table.rows.map((row) => ({ + ...row, + [basePriceColumnId]: [row[basePriceColumnId], 100], + [minPriceColumnId]: [row[minPriceColumnId], 10], + })), + }; + const component = shallow(); + + const [[visConfig]] = component.find(Metric).props().data!; + + expect(visConfig!.value).toMatchInlineSnapshot( + ` + Array [ + "number-28.984375", + "number-100", + ] + ` + ); + }); }); describe('metric grid', () => { const config: Props['config'] = { metric: { - progressDirection: 'vertical', - maxCols: 5, + ...defaultMetricParams, }, dimensions: { metric: basePriceColumnId, @@ -856,8 +918,7 @@ describe('MetricVisComponent', function () { data={table} config={{ metric: { - progressDirection: 'vertical', - maxCols: 5, + ...defaultMetricParams, }, dimensions: { metric: basePriceColumnId, @@ -911,8 +972,7 @@ describe('MetricVisComponent', function () { { const config: Props['config'] = { metric: { - progressDirection: 'vertical', - maxCols: 5, + ...defaultMetricParams, }, dimensions: { metric: '1', @@ -1416,8 +1468,7 @@ describe('MetricVisComponent', function () { = { title: String(title), subtitle, icon: config.metric?.icon ? getIcon(config.metric?.icon) : undefined, extra: renderSecondaryMetric(data.columns, row, config), color: config.metric.color ?? defaultColor, }; - return nonNumericMetric; + return Array.isArray(value) + ? { ...nonNumericMetricBase, value: value.map((v) => formatPrimaryMetric(v)) } + : { ...nonNumericMetricBase, value: formatPrimaryMetric(value) }; } const baseMetric: MetricWNumber = { @@ -335,6 +336,10 @@ export const MetricVis = ({ barBackground: euiThemeVars.euiColorLightShade, emptyBackground: euiThemeVars.euiColorEmptyShade, blendingBackground: euiThemeVars.euiColorEmptyShade, + titlesTextAlign: config.metric.titlesTextAlign, + valuesTextAlign: config.metric.valuesTextAlign, + iconAlign: config.metric.iconAlign, + valueFontSize: config.metric.valueFontSize, }, }, ...(Array.isArray(settingsThemeOverrides) diff --git a/src/plugins/console/server/routes/api/console/autocomplete_entities/index.ts b/src/plugins/console/server/routes/api/console/autocomplete_entities/index.ts index 2d19de0a56e74..677e4b93797c6 100644 --- a/src/plugins/console/server/routes/api/console/autocomplete_entities/index.ts +++ b/src/plugins/console/server/routes/api/console/autocomplete_entities/index.ts @@ -16,7 +16,7 @@ const MAX_RESPONSE_SIZE = 10 * 1024 * 1024; // 10MB const getMappings = async (settings: SettingsToRetrieve, esClient: IScopedClusterClient) => { if (settings.fields && settings.fieldsIndices) { - const mappings = await esClient.asInternalUser.indices.getMapping( + const mappings = await esClient.asCurrentUser.indices.getMapping( { index: settings.fieldsIndices, }, @@ -33,7 +33,7 @@ const getMappings = async (settings: SettingsToRetrieve, esClient: IScopedCluste const getAliases = async (settings: SettingsToRetrieve, esClient: IScopedClusterClient) => { if (settings.indices) { - const aliases = await esClient.asInternalUser.indices.getAlias(); + const aliases = await esClient.asCurrentUser.indices.getAlias(); return aliases; } // If the user doesn't want autocomplete suggestions, then clear any that exist. @@ -42,7 +42,7 @@ const getAliases = async (settings: SettingsToRetrieve, esClient: IScopedCluster const getDataStreams = async (settings: SettingsToRetrieve, esClient: IScopedClusterClient) => { if (settings.dataStreams) { - const dataStreams = await esClient.asInternalUser.indices.getDataStream(); + const dataStreams = await esClient.asCurrentUser.indices.getDataStream(); return dataStreams; } // If the user doesn't want autocomplete suggestions, then clear any that exist. @@ -51,7 +51,7 @@ const getDataStreams = async (settings: SettingsToRetrieve, esClient: IScopedClu const getLegacyTemplates = async (settings: SettingsToRetrieve, esClient: IScopedClusterClient) => { if (settings.templates) { - const legacyTemplates = await esClient.asInternalUser.indices.getTemplate(); + const legacyTemplates = await esClient.asCurrentUser.indices.getTemplate(); return legacyTemplates; } // If the user doesn't want autocomplete suggestions, then clear any that exist. @@ -60,7 +60,7 @@ const getLegacyTemplates = async (settings: SettingsToRetrieve, esClient: IScope const getIndexTemplates = async (settings: SettingsToRetrieve, esClient: IScopedClusterClient) => { if (settings.templates) { - const indexTemplates = await esClient.asInternalUser.indices.getIndexTemplate(); + const indexTemplates = await esClient.asCurrentUser.indices.getIndexTemplate(); return indexTemplates; } // If the user doesn't want autocomplete suggestions, then clear any that exist. @@ -72,7 +72,7 @@ const getComponentTemplates = async ( esClient: IScopedClusterClient ) => { if (settings.templates) { - const componentTemplates = await esClient.asInternalUser.cluster.getComponentTemplate(); + const componentTemplates = await esClient.asCurrentUser.cluster.getComponentTemplate(); return componentTemplates; } // If the user doesn't want autocomplete suggestions, then clear any that exist. diff --git a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx index b2493454d4da2..da9783676cba3 100644 --- a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.test.tsx @@ -157,6 +157,7 @@ describe('useDashboardListingTable', () => { showActivityView: true, }, createdByEnabled: true, + recentlyAccessed: expect.objectContaining({ get: expect.any(Function) }), }; expect(tableListViewTableProps).toEqual(expectedProps); diff --git a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx index 659e5e43930ea..097fc5ea6d866 100644 --- a/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/hooks/use_dashboard_listing_table.tsx @@ -100,6 +100,7 @@ export const useDashboardListingTable = ({ checkForDuplicateDashboardTitle, }, notifications: { toasts }, + dashboardRecentlyAccessed, } = pluginServices.getServices(); const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTableStrings; @@ -302,6 +303,7 @@ export const useDashboardListingTable = ({ title, urlStateEnabled, createdByEnabled: true, + recentlyAccessed: dashboardRecentlyAccessed, }), [ contentEditorValidators, @@ -324,6 +326,7 @@ export const useDashboardListingTable = ({ title, updateItemMeta, urlStateEnabled, + dashboardRecentlyAccessed, ] ); diff --git a/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx b/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx index d3e0ac3459049..6cef6ab1871f4 100644 --- a/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/dashboard_top_nav/internal_dashboard_top_nav.tsx @@ -83,6 +83,7 @@ export function InternalDashboardTopNav({ embeddable: { getStateTransfer }, initializerContext: { allowByValueEmbeddables }, dashboardCapabilities: { saveQuery: allowSaveQuery, showWriteControls }, + dashboardRecentlyAccessed, } = pluginServices.getServices(); const isLabsEnabled = uiSettings.get(UI_SETTINGS.ENABLE_LABS_UI); const { setHeaderActionMenu, onAppLeave } = useDashboardMountContext(); @@ -143,6 +144,11 @@ export function InternalDashboardTopNav({ title, lastSavedId ); + dashboardRecentlyAccessed.add( + getFullEditPath(lastSavedId, viewMode === ViewMode.EDIT), + title, + lastSavedId + ); } return () => subscription.unsubscribe(); }, [ @@ -152,6 +158,7 @@ export function InternalDashboardTopNav({ lastSavedId, viewMode, title, + dashboardRecentlyAccessed, ]); /** diff --git a/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.stub.ts b/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.stub.ts new file mode 100644 index 0000000000000..32f17f01d0e06 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.stub.ts @@ -0,0 +1,22 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { DashboardRecentlyAccessedService } from './types'; + +type DashboardRecentlyAccessedServiceFactory = + PluginServiceFactory; + +export const dashboardRecentlyAccessedServiceFactory: DashboardRecentlyAccessedServiceFactory = + () => { + return { + add: jest.fn(), + get: jest.fn(), + get$: jest.fn(), + }; + }; diff --git a/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.ts b/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.ts new file mode 100644 index 0000000000000..8a80544aacad4 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_recently_accessed/dashboard_recently_accessed.ts @@ -0,0 +1,32 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { RecentlyAccessedService } from '@kbn/recently-accessed'; +import type { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; + +import { DashboardHTTPService } from '../http/types'; +import { DashboardStartDependencies } from '../../plugin'; +import { DashboardRecentlyAccessedService } from './types'; + +interface DashboardRecentlyAccessedRequiredServices { + http: DashboardHTTPService; +} + +export type DashboardBackupServiceFactory = KibanaPluginServiceFactory< + DashboardRecentlyAccessedService, + DashboardStartDependencies, + DashboardRecentlyAccessedRequiredServices +>; + +export const dashboardRecentlyAccessedFactory: DashboardBackupServiceFactory = ( + core, + requiredServices +) => { + const { http } = requiredServices; + return new RecentlyAccessedService().start({ http, key: 'dashboardRecentlyAccessed' }); +}; diff --git a/src/plugins/dashboard/public/services/dashboard_recently_accessed/types.ts b/src/plugins/dashboard/public/services/dashboard_recently_accessed/types.ts new file mode 100644 index 0000000000000..0b27bfe89aa63 --- /dev/null +++ b/src/plugins/dashboard/public/services/dashboard_recently_accessed/types.ts @@ -0,0 +1,11 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { RecentlyAccessed } from '@kbn/recently-accessed'; + +export type DashboardRecentlyAccessedService = RecentlyAccessed; diff --git a/src/plugins/dashboard/public/services/plugin_services.stub.ts b/src/plugins/dashboard/public/services/plugin_services.stub.ts index 8f0f1dd4f5bbc..59c78ac9685b4 100644 --- a/src/plugins/dashboard/public/services/plugin_services.stub.ts +++ b/src/plugins/dashboard/public/services/plugin_services.stub.ts @@ -47,6 +47,7 @@ import { userProfileServiceFactory } from './user_profile/user_profile_service.s import { observabilityAIAssistantServiceStubFactory } from './observability_ai_assistant/observability_ai_assistant_service.stub'; import { noDataPageServiceFactory } from './no_data_page/no_data_page_service.stub'; import { uiActionsServiceFactory } from './ui_actions/ui_actions_service.stub'; +import { dashboardRecentlyAccessedServiceFactory } from './dashboard_recently_accessed/dashboard_recently_accessed.stub'; export const providers: PluginServiceProviders = { dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory), @@ -82,6 +83,7 @@ export const providers: PluginServiceProviders = { uiActions: new PluginServiceProvider(uiActionsServiceFactory), userProfile: new PluginServiceProvider(userProfileServiceFactory), observabilityAIAssistant: new PluginServiceProvider(observabilityAIAssistantServiceStubFactory), + dashboardRecentlyAccessed: new PluginServiceProvider(dashboardRecentlyAccessedServiceFactory), }; export const registry = new PluginServiceRegistry(providers); diff --git a/src/plugins/dashboard/public/services/plugin_services.ts b/src/plugins/dashboard/public/services/plugin_services.ts index 9880308e7c5e5..b46c261fa6507 100644 --- a/src/plugins/dashboard/public/services/plugin_services.ts +++ b/src/plugins/dashboard/public/services/plugin_services.ts @@ -48,6 +48,7 @@ import { noDataPageServiceFactory } from './no_data_page/no_data_page_service'; import { uiActionsServiceFactory } from './ui_actions/ui_actions_service'; import { observabilityAIAssistantServiceFactory } from './observability_ai_assistant/observability_ai_assistant_service'; import { userProfileServiceFactory } from './user_profile/user_profile_service'; +import { dashboardRecentlyAccessedFactory } from './dashboard_recently_accessed/dashboard_recently_accessed'; const providers: PluginServiceProviders = { dashboardContentManagement: new PluginServiceProvider(dashboardContentManagementServiceFactory, [ @@ -96,6 +97,7 @@ const providers: PluginServiceProviders(); diff --git a/src/plugins/dashboard/public/services/types.ts b/src/plugins/dashboard/public/services/types.ts index ebb5fa110d0e2..bb0a8d8d6914d 100644 --- a/src/plugins/dashboard/public/services/types.ts +++ b/src/plugins/dashboard/public/services/types.ts @@ -43,6 +43,7 @@ import { NoDataPageService } from './no_data_page/types'; import { DashboardUiActionsService } from './ui_actions/types'; import { ObservabilityAIAssistantService } from './observability_ai_assistant/types'; import { DashboardUserProfileService } from './user_profile/types'; +import { DashboardRecentlyAccessedService } from './dashboard_recently_accessed/types'; export type DashboardPluginServiceParams = KibanaPluginServiceParams & { initContext: PluginInitializerContext; // need a custom type so that initContext is a required parameter for initializerContext @@ -82,4 +83,5 @@ export interface DashboardServices { uiActions: DashboardUiActionsService; observabilityAIAssistant: ObservabilityAIAssistantService; // TODO: make this optional in follow up userProfile: DashboardUserProfileService; + dashboardRecentlyAccessed: DashboardRecentlyAccessedService; } diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 8659b44b914ff..a29aa10853035 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -83,6 +83,7 @@ "@kbn/esql-utils", "@kbn/lens-embeddable-utils", "@kbn/lens-plugin", + "@kbn/recently-accessed", ], "exclude": ["target/**/*"] } diff --git a/src/plugins/data_views/docs/openapi/bundled.json b/src/plugins/data_views/docs/openapi/bundled.json index 28893b86cef3f..5f2506c46e609 100644 --- a/src/plugins/data_views/docs/openapi/bundled.json +++ b/src/plugins/data_views/docs/openapi/bundled.json @@ -1036,7 +1036,7 @@ "post": { "summary": "Update data view fields metadata in the default space", "operationId": "updateFieldsMetadataDefault", - "description": "Update fields presentation metadata such as count, customLabel and format. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. You can update multiple fields in one request. Updates are merged with persisted metadata. To remove existing metadata, specify null as the value.\n", + "description": "Update fields presentation metadata such as count, customLabel, customDescription, and format. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. You can update multiple fields in one request. Updates are merged with persisted metadata. To remove existing metadata, specify null as the value.\n", "tags": [ "data views" ], @@ -2786,11 +2786,16 @@ } }, "update_field_metadata_request": { - "summary": "Set popularity count for field foo.", + "summary": "Update multiple metadata fields.", "value": { "fields": { - "foo": { - "count": 123 + "field1": { + "count": 123, + "customLabel": "Field 1 label" + }, + "field2": { + "customLabel": "Field 2 label", + "customDescription": "Field 2 description" } } } @@ -3465,7 +3470,22 @@ }, "fieldattrs": { "type": "object", - "description": "A map of field attributes by field name." + "description": "A map of field attributes by field name.", + "properties": { + "count": { + "type": "integer", + "description": "Popularity count for the field." + }, + "customDescription": { + "type": "string", + "description": "Custom description for the field.", + "maxLength": 300 + }, + "customLabel": { + "type": "string", + "description": "Custom label for the field." + } + } }, "fieldformats": { "type": "object", @@ -3532,7 +3552,10 @@ "$ref": "#/components/schemas/allownoindex" }, "fieldAttrs": { - "$ref": "#/components/schemas/fieldattrs" + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/fieldattrs" + } }, "fieldFormats": { "$ref": "#/components/schemas/fieldformats" @@ -3591,7 +3614,10 @@ "$ref": "#/components/schemas/allownoindex" }, "fieldAttrs": { - "$ref": "#/components/schemas/fieldattrs" + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/fieldattrs" + } }, "fieldFormats": { "$ref": "#/components/schemas/fieldformats" diff --git a/src/plugins/data_views/docs/openapi/bundled.yaml b/src/plugins/data_views/docs/openapi/bundled.yaml index dee19e6b1d67b..810f379b272e6 100644 --- a/src/plugins/data_views/docs/openapi/bundled.yaml +++ b/src/plugins/data_views/docs/openapi/bundled.yaml @@ -646,7 +646,7 @@ paths: summary: Update data view fields metadata in the default space operationId: updateFieldsMetadataDefault description: | - Update fields presentation metadata such as count, customLabel and format. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. You can update multiple fields in one request. Updates are merged with persisted metadata. To remove existing metadata, specify null as the value. + Update fields presentation metadata such as count, customLabel, customDescription, and format. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. You can update multiple fields in one request. Updates are merged with persisted metadata. To remove existing metadata, specify null as the value. tags: - data views parameters: @@ -1966,11 +1966,15 @@ components: data_view_id: ff959d40-b880-11e8-a6d9-e546fe2bba5f force: true update_field_metadata_request: - summary: Set popularity count for field foo. + summary: Update multiple metadata fields. value: fields: - foo: + field1: count: 123 + customLabel: Field 1 label + field2: + customLabel: Field 2 label + customDescription: Field 2 description create_runtime_field_request: summary: Create a runtime field. value: @@ -2507,6 +2511,17 @@ components: fieldattrs: type: object description: A map of field attributes by field name. + properties: + count: + type: integer + description: Popularity count for the field. + customDescription: + type: string + description: Custom description for the field. + maxLength: 300 + customLabel: + type: string + description: Custom label for the field. fieldformats: type: object description: A map of field formats by field name. @@ -2556,7 +2571,9 @@ components: allowNoIndex: $ref: '#/components/schemas/allownoindex' fieldAttrs: - $ref: '#/components/schemas/fieldattrs' + type: object + additionalProperties: + $ref: '#/components/schemas/fieldattrs' fieldFormats: $ref: '#/components/schemas/fieldformats' fields: @@ -2596,7 +2613,9 @@ components: allowNoIndex: $ref: '#/components/schemas/allownoindex' fieldAttrs: - $ref: '#/components/schemas/fieldattrs' + type: object + additionalProperties: + $ref: '#/components/schemas/fieldattrs' fieldFormats: $ref: '#/components/schemas/fieldformats' fields: diff --git a/src/plugins/data_views/docs/openapi/components/examples/update_field_metadata_request.yaml b/src/plugins/data_views/docs/openapi/components/examples/update_field_metadata_request.yaml index ffa1eba13150f..e16e7e4d38dcb 100644 --- a/src/plugins/data_views/docs/openapi/components/examples/update_field_metadata_request.yaml +++ b/src/plugins/data_views/docs/openapi/components/examples/update_field_metadata_request.yaml @@ -1,9 +1,14 @@ -summary: Set popularity count for field foo. +summary: Update metadata for multiple fields. value: { "fields": { - "foo": { - "count": 123 + "field1": { + "count": 123, + "customLabel": "Field 1 label" + }, + "field2": { + "customLabel": "Field 2 label", + "customDescription": "Field 2 description" } } } \ No newline at end of file diff --git a/src/plugins/data_views/docs/openapi/components/schemas/create_data_view_request_object.yaml b/src/plugins/data_views/docs/openapi/components/schemas/create_data_view_request_object.yaml index ff2c34ed6d9ad..9ac5e0ddd796a 100644 --- a/src/plugins/data_views/docs/openapi/components/schemas/create_data_view_request_object.yaml +++ b/src/plugins/data_views/docs/openapi/components/schemas/create_data_view_request_object.yaml @@ -12,7 +12,9 @@ properties: allowNoIndex: $ref: 'allownoindex.yaml' fieldAttrs: - $ref: 'fieldattrs.yaml' + type: object + additionalProperties: + $ref: 'fieldattrs.yaml' fieldFormats: $ref: 'fieldformats.yaml' fields: diff --git a/src/plugins/data_views/docs/openapi/components/schemas/data_view_response_object.yaml b/src/plugins/data_views/docs/openapi/components/schemas/data_view_response_object.yaml index 9d3c67201896b..f850c2f7c565e 100644 --- a/src/plugins/data_views/docs/openapi/components/schemas/data_view_response_object.yaml +++ b/src/plugins/data_views/docs/openapi/components/schemas/data_view_response_object.yaml @@ -7,7 +7,9 @@ properties: allowNoIndex: $ref: 'allownoindex.yaml' fieldAttrs: - $ref: 'fieldattrs.yaml' + type: object + additionalProperties: + $ref: 'fieldattrs.yaml' fieldFormats: $ref: 'fieldformats.yaml' fields: diff --git a/src/plugins/data_views/docs/openapi/components/schemas/fieldattrs.yaml b/src/plugins/data_views/docs/openapi/components/schemas/fieldattrs.yaml index ede637d538a92..6d0cecd6b43b5 100644 --- a/src/plugins/data_views/docs/openapi/components/schemas/fieldattrs.yaml +++ b/src/plugins/data_views/docs/openapi/components/schemas/fieldattrs.yaml @@ -1,2 +1,13 @@ type: object -description: A map of field attributes by field name. \ No newline at end of file +description: A map of field attributes by field name. +properties: + count: + type: integer + description: Popularity count for the field. + customDescription: + type: string + description: Custom description for the field. + maxLength: 300 + customLabel: + type: string + description: Custom label for the field. \ No newline at end of file diff --git a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@fields.yaml b/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@fields.yaml index 58dd10b88b5d3..3c2526c073513 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@fields.yaml +++ b/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}@fields.yaml @@ -2,7 +2,7 @@ post: summary: Update data view fields metadata in the default space operationId: updateFieldsMetadataDefault description: > - Update fields presentation metadata such as count, customLabel and format. + Update fields presentation metadata such as count, customLabel, customDescription, and format. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. You can update multiple fields in one request. Updates are merged with persisted metadata. To remove existing metadata, specify null as the value. tags: - data views diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx index bf48c0bb83cf4..800b143213227 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx @@ -56,7 +56,12 @@ export const FieldStatisticsTable = React.memo((props: FieldStatisticsTableProps } = props; const visibleFields = useMemo( - () => convertFieldsToFallbackFields({ fields: columns, additionalFieldGroups }), + () => + convertFieldsToFallbackFields({ + // `discover:searchFieldsFromSource` adds `_source` to the columns, but we should exclude it for Field Statistics + fields: columns.filter((col) => col !== '_source'), + additionalFieldGroups, + }), [additionalFieldGroups, columns] ); const allFallbackFields = useMemo( diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index caba229e9137a..9e061beffc4fc 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -277,7 +277,8 @@ function DiscoverDocumentsComponent({ <> col !== '_source')} /> diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts index 99d911dd14f61..36a07faaef80d 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts @@ -8,6 +8,7 @@ import { difference } from 'lodash'; import { type DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { fieldWildcardFilter } from '@kbn/kibana-utils-plugin/public'; import { isNestedFieldParent } from '@kbn/discover-utils'; @@ -66,14 +67,6 @@ export function getEsqlQueryFieldList(esqlQueryColumns?: DatatableColumn[]): Dat return []; } return esqlQueryColumns.map( - (column) => - new DataViewField({ - name: column.name, - type: column.meta?.type ?? 'unknown', - esTypes: column.meta?.esType ? [column.meta?.esType] : undefined, - searchable: true, - aggregatable: false, - isNull: Boolean(column?.isNull), - }) + (column) => new DataViewField(convertDatatableColumnToDataViewFieldSpec(column)) ); } diff --git a/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx b/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx index 080c2ffed222b..6b705b00c1ba1 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx +++ b/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx @@ -52,6 +52,17 @@ export const exampleDataSourceProfileProvider: DataSourceProfileProvider = { ); }, }), + getDocViewer: (prev) => (params) => { + const recordId = params.record.id; + const prevValue = prev(params); + return { + title: `Record #${recordId}`, + docViewsRegistry: (registry) => { + registry.enableById('doc_view_logs_overview'); + return prevValue.docViewsRegistry(registry); + }, + }; + }, }, resolve: (params) => { let indexPattern: string | undefined; diff --git a/src/plugins/esql/.i18nrc.json b/src/plugins/esql/.i18nrc.json new file mode 100755 index 0000000000000..fce2490c832cf --- /dev/null +++ b/src/plugins/esql/.i18nrc.json @@ -0,0 +1,6 @@ +{ + "prefix": "esql", + "paths": { + "esql": "." + } +} diff --git a/src/plugins/text_based_languages/README.md b/src/plugins/esql/README.md similarity index 86% rename from src/plugins/text_based_languages/README.md rename to src/plugins/esql/README.md index 42d3375220682..05a7406e06a3b 100644 --- a/src/plugins/text_based_languages/README.md +++ b/src/plugins/esql/README.md @@ -1,4 +1,4 @@ -# @kbn/text-based-languages +# @kbn/esql ## Component properties The editor accepts the following properties: @@ -11,8 +11,8 @@ The editor accepts the following properties: - isLoading: As the editor is not responsible for the data fetching request, the consumer could update this property when the data are being fetched. If this property is defined, the query history component will be rendered ``` -To use it on your application, you need to add the textBasedLanguages to your requiredBundles and the @kbn/text-based-languages to your tsconfig.json and use the component like that: -import { TextBasedLangEditor } from '@kbn/text-based-languages/public'; +To use it on your application, you need to add the textBasedLanguages to your requiredBundles and the @kbn/esql to your tsconfig.json and use the component like that: +import { TextBasedLangEditor } from '@kbn/esql/public'; /src/plugins/text_based_languages'], - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/text_based_languages', + roots: ['/src/plugins/esql'], + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/esql', coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/src/plugins/text_based_languages/{common,public,server}/**/*.{js,ts,tsx}', - ], + collectCoverageFrom: ['/src/plugins/esql/{common,public,server}/**/*.{js,ts,tsx}'], setupFiles: ['jest-canvas-mock'], }; diff --git a/src/plugins/text_based_languages/kibana.jsonc b/src/plugins/esql/kibana.jsonc similarity index 82% rename from src/plugins/text_based_languages/kibana.jsonc rename to src/plugins/esql/kibana.jsonc index 5bed408add15a..797b9066e46ae 100644 --- a/src/plugins/text_based_languages/kibana.jsonc +++ b/src/plugins/esql/kibana.jsonc @@ -1,9 +1,9 @@ { "type": "plugin", - "id": "@kbn/text-based-languages", + "id": "@kbn/esql", "owner": "@elastic/kibana-esql", "plugin": { - "id": "textBasedLanguages", + "id": "esql", "server": true, "browser": true, "optionalPlugins": [ diff --git a/src/plugins/text_based_languages/package.json b/src/plugins/esql/package.json similarity index 70% rename from src/plugins/text_based_languages/package.json rename to src/plugins/esql/package.json index a13edb1990192..fe166ce25a71b 100644 --- a/src/plugins/text_based_languages/package.json +++ b/src/plugins/esql/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/text-based-languages", + "name": "@kbn/esql", "private": true, "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0" diff --git a/src/plugins/text_based_languages/public/create_editor.tsx b/src/plugins/esql/public/create_editor.tsx similarity index 100% rename from src/plugins/text_based_languages/public/create_editor.tsx rename to src/plugins/esql/public/create_editor.tsx diff --git a/src/plugins/text_based_languages/public/index.ts b/src/plugins/esql/public/index.ts similarity index 76% rename from src/plugins/text_based_languages/public/index.ts rename to src/plugins/esql/public/index.ts index 697e1d0e1d319..8e797ed591fca 100644 --- a/src/plugins/text_based_languages/public/index.ts +++ b/src/plugins/esql/public/index.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { TextBasedLanguagesPlugin } from './plugin'; +import { EsqlPlugin } from './plugin'; export type { TextBasedLanguagesEditorProps } from '@kbn/text-based-editor'; -export type { TextBasedLanguagesPluginStart } from './types'; +export type { EsqlPluginStart } from './types'; export { TextBasedLangEditor } from './create_editor'; export function plugin() { - return new TextBasedLanguagesPlugin(); + return new EsqlPlugin(); } diff --git a/src/plugins/text_based_languages/public/kibana_services.ts b/src/plugins/esql/public/kibana_services.ts similarity index 100% rename from src/plugins/text_based_languages/public/kibana_services.ts rename to src/plugins/esql/public/kibana_services.ts diff --git a/src/plugins/text_based_languages/public/plugin.ts b/src/plugins/esql/public/plugin.ts similarity index 83% rename from src/plugins/text_based_languages/public/plugin.ts rename to src/plugins/esql/public/plugin.ts index 841641c8fce25..5b78a0b48dc2b 100755 --- a/src/plugins/text_based_languages/public/plugin.ts +++ b/src/plugins/esql/public/plugin.ts @@ -19,22 +19,22 @@ import { } from './triggers'; import { setKibanaServices } from './kibana_services'; -interface TextBasedLanguagesPluginStart { +interface EsqlPluginStart { dataViews: DataViewsPublicPluginStart; expressions: ExpressionsStart; uiActions: UiActionsStart; data: DataPublicPluginStart; } -interface TextBasedLanguagesPluginSetup { +interface EsqlPluginSetup { indexManagement: IndexManagementPluginSetup; uiActions: UiActionsSetup; } -export class TextBasedLanguagesPlugin implements Plugin<{}, void> { +export class EsqlPlugin implements Plugin<{}, void> { private indexManagement?: IndexManagementPluginSetup; - public setup(_: CoreSetup, { indexManagement, uiActions }: TextBasedLanguagesPluginSetup) { + public setup(_: CoreSetup, { indexManagement, uiActions }: EsqlPluginSetup) { this.indexManagement = indexManagement; uiActions.registerTrigger(updateESQLQueryTrigger); @@ -44,7 +44,7 @@ export class TextBasedLanguagesPlugin implements Plugin<{}, void> { public start( core: CoreStart, - { dataViews, expressions, data, uiActions }: TextBasedLanguagesPluginStart + { dataViews, expressions, data, uiActions }: EsqlPluginStart ): void { const appendESQLAction = new UpdateESQLQueryAction(data); uiActions.addTriggerAction(UPDATE_ESQL_QUERY_TRIGGER, appendESQLAction); diff --git a/src/plugins/text_based_languages/public/triggers/index.ts b/src/plugins/esql/public/triggers/index.ts similarity index 100% rename from src/plugins/text_based_languages/public/triggers/index.ts rename to src/plugins/esql/public/triggers/index.ts diff --git a/src/plugins/text_based_languages/public/triggers/update_esql_query_actions.test.ts b/src/plugins/esql/public/triggers/update_esql_query_actions.test.ts similarity index 100% rename from src/plugins/text_based_languages/public/triggers/update_esql_query_actions.test.ts rename to src/plugins/esql/public/triggers/update_esql_query_actions.test.ts diff --git a/src/plugins/text_based_languages/public/triggers/update_esql_query_actions.ts b/src/plugins/esql/public/triggers/update_esql_query_actions.ts similarity index 95% rename from src/plugins/text_based_languages/public/triggers/update_esql_query_actions.ts rename to src/plugins/esql/public/triggers/update_esql_query_actions.ts index 4aa7b015b366b..798ac803d3e3b 100644 --- a/src/plugins/text_based_languages/public/triggers/update_esql_query_actions.ts +++ b/src/plugins/esql/public/triggers/update_esql_query_actions.ts @@ -25,7 +25,7 @@ export class UpdateESQLQueryAction implements Action { constructor(protected readonly data: DataPublicPluginStart) {} public getDisplayName(): string { - return i18n.translate('textBasedLanguages.updateESQLQueryLabel', { + return i18n.translate('esql.updateESQLQueryLabel', { defaultMessage: 'Update the ES|QL query in the editor', }); } diff --git a/src/plugins/text_based_languages/public/triggers/update_esql_query_helpers.ts b/src/plugins/esql/public/triggers/update_esql_query_helpers.ts similarity index 100% rename from src/plugins/text_based_languages/public/triggers/update_esql_query_helpers.ts rename to src/plugins/esql/public/triggers/update_esql_query_helpers.ts diff --git a/src/plugins/text_based_languages/public/triggers/update_esql_query_trigger.ts b/src/plugins/esql/public/triggers/update_esql_query_trigger.ts similarity index 80% rename from src/plugins/text_based_languages/public/triggers/update_esql_query_trigger.ts rename to src/plugins/esql/public/triggers/update_esql_query_trigger.ts index 13164647607ef..79feddcd42c0c 100644 --- a/src/plugins/text_based_languages/public/triggers/update_esql_query_trigger.ts +++ b/src/plugins/esql/public/triggers/update_esql_query_trigger.ts @@ -13,10 +13,10 @@ export const UPDATE_ESQL_QUERY_TRIGGER = 'UPDATE_ESQL_QUERY_TRIGGER'; export const updateESQLQueryTrigger: Trigger = { id: UPDATE_ESQL_QUERY_TRIGGER, - title: i18n.translate('textBasedLanguages.triggers.updateEsqlQueryTrigger', { + title: i18n.translate('esql.triggers.updateEsqlQueryTrigger', { defaultMessage: 'Update ES|QL query', }), - description: i18n.translate('textBasedLanguages.triggers.updateEsqlQueryTriggerDescription', { + description: i18n.translate('esql.triggers.updateEsqlQueryTriggerDescription', { defaultMessage: 'Update ES|QL query with a new one', }), }; diff --git a/src/plugins/text_based_languages/public/types.ts b/src/plugins/esql/public/types.ts similarity index 90% rename from src/plugins/text_based_languages/public/types.ts rename to src/plugins/esql/public/types.ts index c2dd5249d3d19..ef28bddc3c458 100644 --- a/src/plugins/text_based_languages/public/types.ts +++ b/src/plugins/esql/public/types.ts @@ -7,6 +7,6 @@ */ import { TextBasedLanguagesEditorProps } from '@kbn/text-based-editor'; -export interface TextBasedLanguagesPluginStart { +export interface EsqlPluginStart { Editor: React.ComponentType; } diff --git a/src/plugins/text_based_languages/server/index.ts b/src/plugins/esql/server/index.ts similarity index 76% rename from src/plugins/text_based_languages/server/index.ts rename to src/plugins/esql/server/index.ts index bca404b161bf8..fa0a0cec5bdbe 100644 --- a/src/plugins/text_based_languages/server/index.ts +++ b/src/plugins/esql/server/index.ts @@ -7,6 +7,6 @@ */ export const plugin = async () => { - const { TextBasedLanguagesServerPlugin } = await import('./plugin'); - return new TextBasedLanguagesServerPlugin(); + const { EsqlServerPlugin } = await import('./plugin'); + return new EsqlServerPlugin(); }; diff --git a/src/plugins/text_based_languages/server/plugin.ts b/src/plugins/esql/server/plugin.ts similarity index 91% rename from src/plugins/text_based_languages/server/plugin.ts rename to src/plugins/esql/server/plugin.ts index 95a341a467cc5..416c1dacc04e5 100644 --- a/src/plugins/text_based_languages/server/plugin.ts +++ b/src/plugins/esql/server/plugin.ts @@ -9,7 +9,7 @@ import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; import { getUiSettings } from './ui_settings'; -export class TextBasedLanguagesServerPlugin implements Plugin { +export class EsqlServerPlugin implements Plugin { public setup(core: CoreSetup) { core.uiSettings.register(getUiSettings()); return {}; diff --git a/src/plugins/text_based_languages/server/ui_settings.ts b/src/plugins/esql/server/ui_settings.ts similarity index 82% rename from src/plugins/text_based_languages/server/ui_settings.ts rename to src/plugins/esql/server/ui_settings.ts index 32717b0d2cb8c..9c43fdf55cc25 100644 --- a/src/plugins/text_based_languages/server/ui_settings.ts +++ b/src/plugins/esql/server/ui_settings.ts @@ -14,17 +14,17 @@ import { ENABLE_ESQL } from '@kbn/esql-utils'; export const getUiSettings: () => Record = () => ({ [ENABLE_ESQL]: { - name: i18n.translate('textBasedLanguages.advancedSettings.enableESQLTitle', { + name: i18n.translate('esql.advancedSettings.enableESQLTitle', { defaultMessage: 'Enable ES|QL', }), value: true, - description: i18n.translate('textBasedLanguages.advancedSettings.enableESQLDescription', { + description: i18n.translate('esql.advancedSettings.enableESQLDescription', { defaultMessage: 'This setting enables ES|QL in Kibana. By switching it off you will hide the ES|QL user interface from various applications. However, users will be able to access existing ES|QL saved searches, visualizations, etc. If you have feedback on this experience please reach out to us on {link}', values: { link: `` + - i18n.translate('textBasedLanguages.advancedSettings.enableESQL.discussLinkText', { + i18n.translate('esql.advancedSettings.enableESQL.discussLinkText', { defaultMessage: 'https://ela.st/esql-feedback', }) + '', diff --git a/src/plugins/text_based_languages/tsconfig.json b/src/plugins/esql/tsconfig.json similarity index 100% rename from src/plugins/text_based_languages/tsconfig.json rename to src/plugins/esql/tsconfig.json diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index 4ffe9d483a1a4..8d6343337f152 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -135,7 +135,7 @@ export const applicationUsageSchema = { canvas: commonSchema, enterpriseSearch: commonSchema, enterpriseSearchContent: commonSchema, - enterpriseSearchInferenceEndpoints: commonSchema, + enterpriseSearchRelevance: commonSchema, enterpriseSearchAnalytics: commonSchema, enterpriseSearchApplications: commonSchema, enterpriseSearchAISearch: commonSchema, diff --git a/src/plugins/saved_objects_management/public/index.ts b/src/plugins/saved_objects_management/public/index.ts index 6df2288d110b5..37ed13ea749e8 100644 --- a/src/plugins/saved_objects_management/public/index.ts +++ b/src/plugins/saved_objects_management/public/index.ts @@ -13,14 +13,7 @@ export type { SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart, } from './plugin'; -export type { - SavedObjectsManagementActionServiceSetup, - SavedObjectsManagementActionServiceStart, - SavedObjectsManagementColumnServiceSetup, - SavedObjectsManagementColumnServiceStart, - SavedObjectsManagementColumn, - SavedObjectsManagementRecord, -} from './services'; +export type { SavedObjectsManagementColumn, SavedObjectsManagementRecord } from './services'; export { SavedObjectsManagementAction } from './services'; export type { ProcessedImportResponse, FailedImport } from './lib'; export { processImportResponse, getTagFindReferences, parseQuery } from './lib'; diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index 57103921c909a..66bd4f6e9542f 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -17,10 +17,16 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import type { SavedObjectManagementTypeInfo } from '../../common/types'; import { StartDependencies, SavedObjectsManagementPluginStart } from '../plugin'; import { getAllowedTypes } from '../lib'; +import { + SavedObjectsManagementActionServiceStart, + SavedObjectsManagementColumnServiceStart, +} from '../services'; interface MountParams { core: CoreSetup; mountParams: ManagementAppMountParams; + getActionServiceStart: () => SavedObjectsManagementActionServiceStart; + getColumnServiceStart: () => SavedObjectsManagementColumnServiceStart; } let allowedObjectTypes: SavedObjectManagementTypeInfo[] | undefined; @@ -31,8 +37,13 @@ const title = i18n.translate('savedObjectsManagement.objects.savedObjectsTitle', const SavedObjectsEditionPage = lazy(() => import('./saved_objects_edition_page')); const SavedObjectsTablePage = lazy(() => import('./saved_objects_table_page')); -export const mountManagementSection = async ({ core, mountParams }: MountParams) => { - const [coreStart, { data, dataViews, savedObjectsTaggingOss, spaces: spacesApi }, pluginStart] = +export const mountManagementSection = async ({ + core, + mountParams, + getColumnServiceStart, + getActionServiceStart, +}: MountParams) => { + const [coreStart, { data, dataViews, savedObjectsTaggingOss, spaces: spacesApi }] = await core.getStartServices(); const { capabilities } = coreStart.application; const { element, history, setBreadcrumbs } = mountParams; @@ -77,8 +88,8 @@ export const mountManagementSection = async ({ core, mountParams }: MountParams) spacesApi={spacesApi} dataStart={data} dataViewsApi={dataViews} - actionRegistry={pluginStart.actions} - columnRegistry={pluginStart.columns} + actionRegistry={getActionServiceStart()} + columnRegistry={getColumnServiceStart()} allowedTypes={allowedObjectTypes} setBreadcrumbs={setBreadcrumbs} /> diff --git a/src/plugins/saved_objects_management/public/mocks.ts b/src/plugins/saved_objects_management/public/mocks.ts index 82fe0d419855c..51b93f04c5826 100644 --- a/src/plugins/saved_objects_management/public/mocks.ts +++ b/src/plugins/saved_objects_management/public/mocks.ts @@ -6,22 +6,18 @@ * Side Public License, v 1. */ -import { actionServiceMock } from './services/action_service.mock'; -import { columnServiceMock } from './services/column_service.mock'; -import { SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart } from './plugin'; +import type { + SavedObjectsManagementPluginSetup, + SavedObjectsManagementPluginStart, +} from './plugin'; const createSetupContractMock = (): jest.Mocked => { - const mock = { - actions: actionServiceMock.createSetup(), - columns: columnServiceMock.createSetup(), - }; + const mock = {}; return mock; }; const createStartContractMock = (): jest.Mocked => { const mock = { - actions: actionServiceMock.createStart(), - columns: columnServiceMock.createStart(), getAllowedTypes: jest.fn(), getRelationships: jest.fn(), getSavedObjectLabel: jest.fn(), diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index eaefe96a0916a..855ea9dd7c91e 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -16,10 +16,8 @@ import { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public'; import { SavedObjectsManagementActionService, - SavedObjectsManagementActionServiceSetup, SavedObjectsManagementActionServiceStart, SavedObjectsManagementColumnService, - SavedObjectsManagementColumnServiceSetup, SavedObjectsManagementColumnServiceStart, } from './services'; @@ -28,21 +26,18 @@ import type { v1 } from '../common'; import { SavedObjectManagementTypeInfo } from './types'; import { getAllowedTypes, + getDefaultTitle, getRelationships, getSavedObjectLabel, - getDefaultTitle, - parseQuery, getTagFindReferences, + parseQuery, } from './lib'; -export interface SavedObjectsManagementPluginSetup { - actions: SavedObjectsManagementActionServiceSetup; - columns: SavedObjectsManagementColumnServiceSetup; -} +// keeping the interface to reduce work if we want to add back APIs later +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SavedObjectsManagementPluginSetup {} export interface SavedObjectsManagementPluginStart { - actions: SavedObjectsManagementActionServiceStart; - columns: SavedObjectsManagementColumnServiceStart; getAllowedTypes: () => Promise; getRelationships: ( type: string, @@ -79,6 +74,9 @@ export class SavedObjectsManagementPlugin private actionService = new SavedObjectsManagementActionService(); private columnService = new SavedObjectsManagementColumnService(); + private actionServiceStart?: SavedObjectsManagementActionServiceStart; + private columnServiceStart?: SavedObjectsManagementColumnServiceStart; + public setup( core: CoreSetup, { home, management }: SetupDependencies @@ -114,6 +112,8 @@ export class SavedObjectsManagementPlugin return mountManagementSection({ core, mountParams, + getColumnServiceStart: () => this.columnServiceStart!, + getActionServiceStart: () => this.actionServiceStart!, }); }, }); @@ -125,12 +125,10 @@ export class SavedObjectsManagementPlugin } public start(_core: CoreStart, { spaces: spacesApi }: StartDependencies) { - const actionStart = this.actionService.start(spacesApi); - const columnStart = this.columnService.start(spacesApi); + this.actionServiceStart = this.actionService.start(spacesApi); + this.columnServiceStart = this.columnService.start(spacesApi); return { - actions: actionStart, - columns: columnStart, getAllowedTypes: () => getAllowedTypes(_core.http), getRelationships: (type: string, id: string, savedObjectTypes: string[]) => getRelationships(_core.http, type, id, savedObjectTypes), diff --git a/src/plugins/saved_objects_management/public/services/action_service.ts b/src/plugins/saved_objects_management/public/services/action_service.ts index b255ee9a7fa80..83f968a846113 100644 --- a/src/plugins/saved_objects_management/public/services/action_service.ts +++ b/src/plugins/saved_objects_management/public/services/action_service.ts @@ -36,30 +36,25 @@ export class SavedObjectsManagementActionService { setup(): SavedObjectsManagementActionServiceSetup { return { - register: (action) => { - if (this.actions.has(action.id)) { - throw new Error(`Saved Objects Management Action with id '${action.id}' already exists`); - } - this.actions.set(action.id, action); - }, + register: (action) => this.register(action), }; } start(spacesApi?: SpacesApi): SavedObjectsManagementActionServiceStart { if (spacesApi && !spacesApi.hasOnlyDefaultSpace) { - registerSpacesApiActions(this, spacesApi); + this.register(new ShareToSpaceSavedObjectsManagementAction(spacesApi.ui)); + this.register(new CopyToSpaceSavedObjectsManagementAction(spacesApi.ui)); } return { has: (actionId) => this.actions.has(actionId), getAll: () => [...this.actions.values()], }; } -} -function registerSpacesApiActions( - service: SavedObjectsManagementActionService, - spacesApi: SpacesApi -) { - service.setup().register(new ShareToSpaceSavedObjectsManagementAction(spacesApi.ui)); - service.setup().register(new CopyToSpaceSavedObjectsManagementAction(spacesApi.ui)); + private register(action: SavedObjectsManagementAction) { + if (this.actions.has(action.id)) { + throw new Error(`Saved Objects Management Action with id '${action.id}' already exists`); + } + this.actions.set(action.id, action); + } } diff --git a/src/plugins/saved_objects_management/public/services/column_service.ts b/src/plugins/saved_objects_management/public/services/column_service.ts index 665866df1b4b0..519704d258c49 100644 --- a/src/plugins/saved_objects_management/public/services/column_service.ts +++ b/src/plugins/saved_objects_management/public/services/column_service.ts @@ -29,28 +29,23 @@ export class SavedObjectsManagementColumnService { setup(): SavedObjectsManagementColumnServiceSetup { return { - register: (column) => { - if (this.columns.has(column.id)) { - throw new Error(`Saved Objects Management Column with id '${column.id}' already exists`); - } - this.columns.set(column.id, column); - }, + register: (column) => this.register(column), }; } start(spacesApi?: SpacesApi): SavedObjectsManagementColumnServiceStart { if (spacesApi && !spacesApi.hasOnlyDefaultSpace) { - registerSpacesApiColumns(this, spacesApi); + this.register(new ShareToSpaceSavedObjectsManagementColumn(spacesApi.ui)); } return { getAll: () => [...this.columns.values()], }; } -} -function registerSpacesApiColumns( - service: SavedObjectsManagementColumnService, - spacesApi: SpacesApi -) { - service.setup().register(new ShareToSpaceSavedObjectsManagementColumn(spacesApi.ui)); + private register(column: SavedObjectsManagementColumn) { + if (this.columns.has(column.id)) { + throw new Error(`Saved Objects Management Column with id '${column.id}' already exists`); + } + this.columns.set(column.id, column); + } } diff --git a/src/plugins/saved_objects_management/public/services/index.ts b/src/plugins/saved_objects_management/public/services/index.ts index 997a1b96b418f..9d6f96cee0cfb 100644 --- a/src/plugins/saved_objects_management/public/services/index.ts +++ b/src/plugins/saved_objects_management/public/services/index.ts @@ -6,15 +6,9 @@ * Side Public License, v 1. */ -export type { - SavedObjectsManagementActionServiceStart, - SavedObjectsManagementActionServiceSetup, -} from './action_service'; +export type { SavedObjectsManagementActionServiceStart } from './action_service'; export { SavedObjectsManagementActionService } from './action_service'; -export type { - SavedObjectsManagementColumnServiceStart, - SavedObjectsManagementColumnServiceSetup, -} from './column_service'; +export type { SavedObjectsManagementColumnServiceStart } from './column_service'; export { SavedObjectsManagementColumnService } from './column_service'; export type { SavedObjectsManagementRecord } from './types'; export { SavedObjectsManagementColumn, SavedObjectsManagementAction } from './types'; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 22e75e5d4b658..00c4a48da72fa 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -2098,7 +2098,7 @@ } } }, - "enterpriseSearchInferenceEndpoints": { + "enterpriseSearchRelevance": { "properties": { "appId": { "type": "keyword", diff --git a/src/plugins/text_based_languages/.i18nrc.json b/src/plugins/text_based_languages/.i18nrc.json deleted file mode 100755 index d5a020d5dd392..0000000000000 --- a/src/plugins/text_based_languages/.i18nrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "prefix": "textBasedLanguages", - "paths": { - "textBasedLanguages": "." - } -} diff --git a/src/plugins/unified_doc_viewer/kibana.jsonc b/src/plugins/unified_doc_viewer/kibana.jsonc index 2361a10120e9b..e2febffda4df6 100644 --- a/src/plugins/unified_doc_viewer/kibana.jsonc +++ b/src/plugins/unified_doc_viewer/kibana.jsonc @@ -8,7 +8,7 @@ "server": false, "browser": true, "requiredBundles": ["kibanaUtils"], - "requiredPlugins": ["data", "discoverShared", "fieldFormats"], + "requiredPlugins": ["data", "discoverShared", "fieldFormats", "share"], "optionalPlugins": ["fieldsMetadata"] } } diff --git a/src/plugins/unified_doc_viewer/public/__mocks__/services.ts b/src/plugins/unified_doc_viewer/public/__mocks__/services.ts index 8496b919b38f0..29ae5f2981a6e 100644 --- a/src/plugins/unified_doc_viewer/public/__mocks__/services.ts +++ b/src/plugins/unified_doc_viewer/public/__mocks__/services.ts @@ -12,6 +12,7 @@ import { discoverSharedPluginMock } from '@kbn/discover-shared-plugin/public/moc import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; import { fieldsMetadataPluginPublicMock } from '@kbn/fields-metadata-plugin/public/mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; import type { UnifiedDocViewerServices, UnifiedDocViewerStart } from '../types'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { DocViewsRegistry } from '@kbn/unified-doc-viewer'; @@ -29,4 +30,5 @@ export const mockUnifiedDocViewerServices: jest.Mocked storage: new Storage(localStorage), uiSettings: uiSettingsServiceMock.createStartContract(), unifiedDocViewer: mockUnifiedDocViewer, + share: sharePluginMock.createStartContract(), }; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx index 7b9379654cec1..a0f4ba8e993ff 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx @@ -18,6 +18,8 @@ const DATASET_NAME = 'logs.overview'; const NAMESPACE = 'default'; const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; const NOW = Date.now(); +const MORE_THAN_1024_CHARS = + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?'; const dataView = { fields: { @@ -82,7 +84,7 @@ const fullHit = buildDataTableRecord( cloud: { provider: ['gcp'], region: 'us-central-1', - availability_zone: 'us-central-1a', + availability_zone: MORE_THAN_1024_CHARS, project: { id: 'elastic-project', }, @@ -92,6 +94,9 @@ const fullHit = buildDataTableRecord( }, 'agent.name': 'node', }, + ignored_field_values: { + 'cloud.availability_zone': [MORE_THAN_1024_CHARS], + }, }, dataView ); @@ -159,4 +164,37 @@ describe('LogsOverview', () => { expect(screen.queryByTestId('unifiedDocViewLogsOverviewLogShipper')).toBeInTheDocument(); }); }); + describe('Degraded Fields section', () => { + it('should load the degraded fields container when present', async () => { + expect( + screen.queryByTestId('unifiedDocViewLogsOverviewDegradedFieldsAccordion') + ).toBeInTheDocument(); + expect( + screen.queryByTestId('unifiedDocViewLogsOverviewDegradedFieldsTechPreview') + ).toBeInTheDocument(); + expect( + screen.queryByTestId('unifiedDocViewLogsOverviewDegradedFieldTitleCount') + ).toBeInTheDocument(); + + // The accordion must be closed by default + const accordion = screen.queryByTestId('unifiedDocViewLogsOverviewDegradedFieldsAccordion1'); + + if (accordion === null) { + return; + } + const button = accordion.querySelector('button'); + + if (button === null) { + return; + } + // Check the aria-expanded property of the button + const isExpanded = button.getAttribute('aria-expanded'); + expect(isExpanded).toBe('false'); + + button.click(); + expect( + screen.queryByTestId('unifiedDocViewLogsOverviewDegradedFieldsQualityIssuesTable') + ).toBeInTheDocument(); + }); + }); }); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx index c0161d112e955..b46570f4f0d37 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx @@ -15,6 +15,7 @@ import { LogsOverviewHighlights } from './logs_overview_highlights'; import { FieldActionsProvider } from '../../hooks/use_field_actions'; import { getUnifiedDocViewerServices } from '../../plugin'; import { LogsOverviewAIAssistant } from './logs_overview_ai_assistant'; +import { LogsOverviewDegradedFields } from './logs_overview_degraded_fields'; export function LogsOverview({ columns, @@ -38,6 +39,7 @@ export function LogsOverview({ + ); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_degraded_fields.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_degraded_fields.tsx new file mode 100644 index 0000000000000..976ef71c6b647 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_degraded_fields.tsx @@ -0,0 +1,321 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ +import React, { useMemo, useState } from 'react'; +import { DataTableRecord } from '@kbn/discover-utils'; +import { + EuiAccordion, + EuiBadge, + EuiBetaBadge, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiTitle, + EuiBasicTable, + useGeneratedHtmlId, + EuiBasicTableColumn, + EuiHeaderLink, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { orderBy } from 'lodash'; +import { getRouterLinkProps } from '@kbn/router-utils'; +import { DATA_QUALITY_LOCATOR_ID, DataQualityLocatorParams } from '@kbn/deeplinks-observability'; +import { BrowserUrlService } from '@kbn/share-plugin/public'; +import { getUnifiedDocViewerServices } from '../../plugin'; + +type Direction = 'asc' | 'desc'; +type SortField = 'issue' | 'values'; + +const DEFAULT_SORT_FIELD = 'issue'; +const DEFAULT_SORT_DIRECTION = 'asc'; +const DEFAULT_ROWS_PER_PAGE = 5; + +interface DegradedField { + issue: string; + values: string[]; +} + +interface ParamsForLocator { + dataStreamType: string; + dataStreamName: string; + dataStreamNamespace: string; + rawName: string; +} + +interface TableOptions { + page: { + index: number; + size: number; + }; + sort: { + field: SortField; + direction: Direction; + }; +} + +const DEFAULT_TABLE_OPTIONS: TableOptions = { + page: { + index: 0, + size: 0, + }, + sort: { + field: DEFAULT_SORT_FIELD, + direction: DEFAULT_SORT_DIRECTION, + }, +}; + +const qualityIssuesAccordionTitle = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.accordion.title.qualityIssues', + { + defaultMessage: 'Quality Issues', + } +); + +const qualityIssuesAccordionTechPreviewBadge = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.accordion.title.techPreview', + { + defaultMessage: 'TECH PREVIEW', + } +); + +const issueColumnName = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.accordion.qualityIssues.table.field', + { + defaultMessage: 'Issue', + } +); + +const valuesColumnName = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.accordion.qualityIssues.table.values', + { + defaultMessage: 'Values', + } +); + +const textFieldIgnored = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.accordion.qualityIssues.table.textIgnored', + { + defaultMessage: 'field ignored', + } +); + +export const datasetQualityLinkTitle = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.accordion.qualityIssues.table.datasetQualityLinkTitle', + { + defaultMessage: 'Data set details', + } +); + +export const LogsOverviewDegradedFields = ({ rawDoc }: { rawDoc: DataTableRecord['raw'] }) => { + const { ignored_field_values: ignoredFieldValues = {}, fields: sourceFields = {} } = rawDoc; + const countOfDegradedFields = Object.keys(ignoredFieldValues)?.length; + + const columns = getDegradedFieldsColumns(); + const tableData = getDataFormattedForTable(ignoredFieldValues); + + const paramsForLocator = getParamsForLocator(sourceFields); + + const accordionId = useGeneratedHtmlId({ + prefix: qualityIssuesAccordionTitle, + }); + + const [tableOptions, setTableOptions] = useState(DEFAULT_TABLE_OPTIONS); + + const onTableChange = (options: { + page: { index: number; size: number }; + sort?: { field: SortField; direction: Direction }; + }) => { + setTableOptions({ + page: { + index: options.page.index, + size: options.page.size, + }, + sort: { + field: options.sort?.field ?? DEFAULT_SORT_FIELD, + direction: options.sort?.direction ?? DEFAULT_SORT_DIRECTION, + }, + }); + }; + + const pagination = useMemo( + () => ({ + pageIndex: tableOptions.page.index, + pageSize: DEFAULT_ROWS_PER_PAGE, + totalItemCount: tableData?.length ?? 0, + hidePerPageOptions: true, + }), + [tableData, tableOptions] + ); + + const renderedItems = useMemo(() => { + const sortedItems = orderBy(tableData, tableOptions.sort.field, tableOptions.sort.direction); + return sortedItems.slice( + tableOptions.page.index * DEFAULT_ROWS_PER_PAGE, + (tableOptions.page.index + 1) * DEFAULT_ROWS_PER_PAGE + ); + }, [tableData, tableOptions]); + + const { share } = getUnifiedDocViewerServices(); + const { url: urlService } = share; + + const accordionTitle = ( + + + +

{qualityIssuesAccordionTitle}

+
+
+ + + {countOfDegradedFields} + + + + + +
+ ); + + return countOfDegradedFields > 0 ? ( + <> + + } + data-test-subj="unifiedDocViewLogsOverviewDegradedFieldsAccordion" + > + + + + + ) : null; +}; + +const getDegradedFieldsColumns = (): Array> => [ + { + name: issueColumnName, + sortable: true, + field: 'issue', + render: (issue: string) => { + return ( + <> + {issue} {textFieldIgnored} + + ); + }, + }, + { + name: valuesColumnName, + sortable: true, + field: 'values', + render: (values: string[]) => { + return values.map((value, idx) => {value}); + }, + }, +]; + +const getDataFormattedForTable = ( + ignoredFieldValues: Record +): DegradedField[] => { + return Object.entries(ignoredFieldValues).map(([field, values]) => ({ + issue: field, + values, + })); +}; + +const getParamsForLocator = ( + sourceFields: DataTableRecord['raw']['fields'] +): ParamsForLocator | undefined => { + if (sourceFields) { + const dataStreamTypeArr = sourceFields['data_stream.type']; + const dataStreamType = dataStreamTypeArr ? dataStreamTypeArr[0] : undefined; + const dataStreamNameArr = sourceFields['data_stream.dataset']; + const dataStreamName = dataStreamNameArr ? dataStreamNameArr[0] : undefined; + const dataStreamNamespaceArr = sourceFields['data_stream.namespace']; + const dataStreamNamespace = dataStreamNamespaceArr ? dataStreamNamespaceArr[0] : undefined; + let rawName; + + if (dataStreamType && dataStreamName && dataStreamNamespace) { + rawName = `${dataStreamType}-${dataStreamName}-${dataStreamNamespace}`; + } + + if (rawName) { + return { + dataStreamType, + dataStreamName, + dataStreamNamespace, + rawName, + }; + } + } +}; + +const DatasetQualityLink = React.memo( + ({ + urlService, + paramsForLocator, + }: { + urlService: BrowserUrlService; + paramsForLocator?: ParamsForLocator; + }) => { + const locator = urlService.locators.get(DATA_QUALITY_LOCATOR_ID); + const locatorParams: DataQualityLocatorParams = paramsForLocator + ? { + flyout: { + dataset: { + rawName: paramsForLocator.rawName, + type: paramsForLocator.dataStreamType, + name: paramsForLocator.dataStreamName, + namespace: paramsForLocator.dataStreamNamespace, + }, + }, + } + : {}; + + const datasetQualityUrl = locator?.getRedirectUrl(locatorParams); + + const navigateToDatasetQuality = () => { + locator?.navigate(locatorParams); + }; + + const datasetQualityLinkProps = getRouterLinkProps({ + href: datasetQualityUrl, + onClick: navigateToDatasetQuality, + }); + + return paramsForLocator ? ( + + {datasetQualityLinkTitle} + + ) : null; + } +); diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index 13027a2541084..9c4d7117c37dd 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -19,6 +19,7 @@ import { CoreStart } from '@kbn/core/public'; import { dynamic } from '@kbn/shared-ux-utility'; import { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public'; import { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; +import { SharePluginStart } from '@kbn/share-plugin/public'; import type { UnifiedDocViewerServices } from './types'; export const [getUnifiedDocViewerServices, setUnifiedDocViewerServices] = @@ -52,6 +53,7 @@ export interface UnifiedDocViewerStartDeps { discoverShared: DiscoverSharedPublicStart; fieldFormats: FieldFormatsStart; fieldsMetadata: FieldsMetadataPublicStart; + share: SharePluginStart; } export class UnifiedDocViewerPublicPlugin @@ -121,7 +123,7 @@ export class UnifiedDocViewerPublicPlugin public start(core: CoreStart, deps: UnifiedDocViewerStartDeps) { const { analytics, uiSettings } = core; - const { data, discoverShared, fieldFormats, fieldsMetadata } = deps; + const { data, discoverShared, fieldFormats, fieldsMetadata, share } = deps; const storage = new Storage(localStorage); const unifiedDocViewer = { registry: this.docViewsRegistry, @@ -135,6 +137,7 @@ export class UnifiedDocViewerPublicPlugin storage, uiSettings, unifiedDocViewer, + share, }; setUnifiedDocViewerServices(services); return unifiedDocViewer; diff --git a/src/plugins/unified_doc_viewer/public/types.ts b/src/plugins/unified_doc_viewer/public/types.ts index c19c60da72b13..e471fa87b85c1 100644 --- a/src/plugins/unified_doc_viewer/public/types.ts +++ b/src/plugins/unified_doc_viewer/public/types.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - export type { JsonCodeEditorProps } from './components'; export type { EsDocSearchProps } from './hooks'; export type { UnifiedDocViewerSetup, UnifiedDocViewerStart } from './plugin'; @@ -17,6 +16,7 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { UnifiedDocViewerStart } from './plugin'; export interface UnifiedDocViewerServices { @@ -28,4 +28,5 @@ export interface UnifiedDocViewerServices { storage: Storage; uiSettings: IUiSettingsClient; unifiedDocViewer: UnifiedDocViewerStart; + share: SharePluginStart; } diff --git a/src/plugins/unified_doc_viewer/tsconfig.json b/src/plugins/unified_doc_viewer/tsconfig.json index fbe2ac83c5f1a..3b271744ed4af 100644 --- a/src/plugins/unified_doc_viewer/tsconfig.json +++ b/src/plugins/unified_doc_viewer/tsconfig.json @@ -32,7 +32,10 @@ "@kbn/discover-shared-plugin", "@kbn/fields-metadata-plugin", "@kbn/unified-data-table", - "@kbn/core-notifications-browser" + "@kbn/core-notifications-browser", + "@kbn/deeplinks-observability", + "@kbn/share-plugin", + "@kbn/router-utils" ], "exclude": [ "target/**/*", diff --git a/src/plugins/unified_histogram/public/chart/histogram.tsx b/src/plugins/unified_histogram/public/chart/histogram.tsx index 50c8f7242c589..70e1f039bc3ba 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.tsx @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { useEuiTheme, useResizeObserver } from '@elastic/eui'; +import { useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState } from 'react'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { DefaultInspectorAdapters, Datatable } from '@kbn/expressions-plugin/common'; import type { IKibanaSearchResponse } from '@kbn/search-types'; @@ -106,7 +106,6 @@ export function Histogram({ abortController, }: HistogramProps) { const [bucketInterval, setBucketInterval] = useState(); - const [chartSize, setChartSize] = useState('100%'); const { timeRangeText, timeRangeDisplay } = useTimeRange({ uiSettings, bucketInterval, @@ -115,19 +114,8 @@ export function Histogram({ isPlainRecord, timeField: dataView.timeFieldName, }); - const chartRef = useRef(null); - const { height: containerHeight, width: containerWidth } = useResizeObserver(chartRef.current); const { attributes } = visContext; - useEffect(() => { - if (attributes.visualizationType === 'lnsMetric') { - const size = containerHeight < containerWidth ? containerHeight : containerWidth; - setChartSize(`${size}px`); - } else { - setChartSize('100%'); - } - }, [attributes, containerHeight, containerWidth]); - const onLoad = useStableCallback( ( isLoading: boolean, @@ -197,7 +185,7 @@ export function Histogram({ } & .lnsExpressionRenderer { - width: ${chartSize}; + width: ${attributes.visualizationType === 'lnsMetric' ? '90$' : '100%'}; margin: auto; box-shadow: ${attributes.visualizationType === 'lnsMetric' ? boxShadow : 'none'}; } @@ -222,7 +210,6 @@ export function Histogram({ data-request-data={requestData} data-suggestion-type={visContext.suggestionType} css={chartCss} - ref={chartRef} > { + let typeServiceStart: TypesStart; + + beforeEach(() => { + const typeService = new TypesService(); + + typeServiceStart = typeService.start(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('invoking the compatibility function returns false when initialized with types that are not grouped as agg visualizations', async () => { + jest.spyOn(typeServiceStart, 'all').mockReturnValue([]); + + const addAggVisualizationPanelAction = new AddAggVisualizationPanelAction(typeServiceStart); + + expect( + await addAggVisualizationPanelAction.isCompatible({ embeddable: mockCompatibleEmbeddableAPI }) + ).toBe(false); + }); + + test('invoking the compatibility function returns true when the registered agg visualizations type does not have creation disabled', async () => { + jest.spyOn(typeServiceStart, 'all').mockReturnValue([ + { + group: VisGroups.AGGBASED, + disableCreate: false, + name: 'test visualization', + } as BaseVisType, + ]); + + const addAggVisualizationPanelAction = new AddAggVisualizationPanelAction(typeServiceStart); + + expect( + await addAggVisualizationPanelAction.isCompatible({ embeddable: mockCompatibleEmbeddableAPI }) + ).toBe(true); + }); +}); diff --git a/src/plugins/visualizations/public/actions/add_agg_vis_action.ts b/src/plugins/visualizations/public/actions/add_agg_vis_action.ts index 62c8e3654db6e..d0b7b2d9a7f6d 100644 --- a/src/plugins/visualizations/public/actions/add_agg_vis_action.ts +++ b/src/plugins/visualizations/public/actions/add_agg_vis_action.ts @@ -17,11 +17,13 @@ import { COMMON_EMBEDDABLE_GROUPING } from '@kbn/embeddable-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { apiHasType } from '@kbn/presentation-publishing'; import { apiCanAddNewPanel, CanAddNewPanel } from '@kbn/presentation-containers'; +import { VisGroups } from '../vis_types/vis_groups_enum'; +import type { TypesStart } from '../vis_types/types_service'; import { showNewVisModal } from '../wizard/show_new_vis'; -const ADD_AGG_VIS_ACTION_ID = 'ADD_AGG_VIS'; +export const ADD_AGG_VIS_ACTION_ID = 'ADD_AGG_VIS'; -type AddAggVisualizationPanelActionApi = HasType & CanAddNewPanel & HasAppContext; +export type AddAggVisualizationPanelActionApi = HasType & CanAddNewPanel & HasAppContext; const isApiCompatible = (api: unknown | null): api is AddAggVisualizationPanelActionApi => { return apiHasType(api) && apiCanAddNewPanel(api) && apiHasAppContext(api); @@ -31,10 +33,15 @@ export class AddAggVisualizationPanelAction implements Action { + return !type.disableCreate && type.group === VisGroups.AGGBASED; + }); + } public getIconType() { return 'visualizeApp'; @@ -47,7 +54,8 @@ export class AddAggVisualizationPanelAction implements Action { diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index c97ff8f4eba45..bb931a072f192 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -400,8 +400,6 @@ export class VisualizationsPlugin uiActions.registerTrigger(dashboardVisualizationPanelTrigger); const editInLensAction = new EditInLensAction(data.query.timefilter.timefilter); uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editInLensAction); - const addAggVisAction = new AddAggVisualizationPanelAction(); - uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addAggVisAction); const embeddableFactory = new VisualizeEmbeddableFactory({ start }); embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); @@ -499,6 +497,9 @@ export class VisualizationsPlugin setSavedObjectTagging(savedObjectsTaggingOss); } + const addAggVisAction = new AddAggVisualizationPanelAction(types); + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addAggVisAction); + return { ...types, showNewVisModal, diff --git a/test/functional/apps/discover/context_awareness/_data_source_profile.ts b/test/functional/apps/discover/context_awareness/_data_source_profile.ts index d203f33c887e8..f3f7e35d7030b 100644 --- a/test/functional/apps/discover/context_awareness/_data_source_profile.ts +++ b/test/functional/apps/discover/context_awareness/_data_source_profile.ts @@ -14,6 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker', 'discover', 'unifiedFieldList']); const testSubjects = getService('testSubjects'); const dataViews = getService('dataViews'); + const dataGrid = getService('dataGrid'); describe('data source profile', () => { describe('ES|QL mode', () => { @@ -58,6 +59,40 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await logLevels[2].getVisibleText()).to.be('Info'); }); }); + + describe('doc viewer extension', () => { + it('should not render custom doc viewer view', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-* | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.missingOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be('Result'); + }); + + it('should render custom doc viewer view', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-logs | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.existOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be('Record #0'); + }); + }); }); describe('data view mode', () => { @@ -92,6 +127,32 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await logLevels[2].getVisibleText()).to.be('Info'); }); }); + + describe('doc viewer extension', () => { + it('should not render custom doc viewer view', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-*'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.missingOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be('Document'); + }); + + it('should render custom doc viewer view', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-logs'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.existOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be( + 'Record #my-example-logs::XdQFDpABfGznVC1bCHLo::' + ); + }); + }); }); }); } diff --git a/test/functional/apps/discover/group3/_doc_viewer.ts b/test/functional/apps/discover/group3/_doc_viewer.ts index 140129c6a251f..ddcdf5c4e40b7 100644 --- a/test/functional/apps/discover/group3/_doc_viewer.ts +++ b/test/functional/apps/discover/group3/_doc_viewer.ts @@ -61,7 +61,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return (await find.allByCssSelector('.kbnDocViewer__fieldName')).length === 4; }); - await PageObjects.discover.findFieldByNameInDocViewer('.s'); + await PageObjects.discover.findFieldByNameInDocViewer('.sr'); await retry.waitFor('second updates', async () => { return (await find.allByCssSelector('.kbnDocViewer__fieldName')).length === 2; @@ -70,7 +70,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should be able to search by wildcard', async function () { await PageObjects.discover.findFieldByNameInDocViewer('relatedContent*image'); - await retry.waitFor('updates', async () => { return (await find.allByCssSelector('.kbnDocViewer__fieldName')).length === 2; }); @@ -78,12 +77,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should be able to search with spaces as wildcard', async function () { await PageObjects.discover.findFieldByNameInDocViewer('relatedContent image'); - await retry.waitFor('updates', async () => { return (await find.allByCssSelector('.kbnDocViewer__fieldName')).length === 4; }); }); + it('should be able to search with fuzzy search (1 typo)', async function () { + await PageObjects.discover.findFieldByNameInDocViewer('rel4tedContent.art'); + + await retry.waitFor('updates', async () => { + return (await find.allByCssSelector('.kbnDocViewer__fieldName')).length === 3; + }); + }); + it('should ignore empty search', async function () { await PageObjects.discover.findFieldByNameInDocViewer(' '); // only spaces diff --git a/test/functional/apps/discover/group6/_field_stats_table.ts b/test/functional/apps/discover/group6/_field_stats_table.ts new file mode 100644 index 0000000000000..45b05d7ddc4bf --- /dev/null +++ b/test/functional/apps/discover/group6/_field_stats_table.ts @@ -0,0 +1,72 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'header']); + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + const kibanaServer = getService('kibanaServer'); + const security = getService('security'); + const defaultSettings = { + defaultIndex: 'logstash-*', + }; + + describe('discover field statistics table', function () { + before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + }); + + after(async () => { + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.savedObjects.cleanStandardList(); + }); + + [true, false].forEach((shouldSearchFieldsFromSource) => { + describe(`discover:searchFieldsFromSource: ${shouldSearchFieldsFromSource}`, function () { + before(async function () { + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update({ + ...defaultSettings, + 'discover:searchFieldsFromSource': shouldSearchFieldsFromSource, + }); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + }); + + after(async () => { + await kibanaServer.uiSettings.replace({}); + }); + + it('should show Field Statistics data in data view mode', async () => { + await testSubjects.click('dscViewModeFieldStatsButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('dataVisualizerTableContainer'); + + await testSubjects.click('dscViewModeDocumentButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('discoverDocTable'); + }); + + it('should show Field Statistics data in ES|QL mode', async () => { + await PageObjects.discover.selectTextBaseLang(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await testSubjects.click('dscViewModeFieldStatsButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('dataVisualizerTableContainer'); + }); + }); + }); + }); +} diff --git a/test/functional/apps/discover/group6/_sidebar.ts b/test/functional/apps/discover/group6/_sidebar.ts index ab2dc70e9e782..a2841bddf8777 100644 --- a/test/functional/apps/discover/group6/_sidebar.ts +++ b/test/functional/apps/discover/group6/_sidebar.ts @@ -223,6 +223,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); + it('should be able to search with fuzzy search (1 typo)', async function () { + await PageObjects.unifiedFieldList.findFieldByName('rel4tedContent.art'); + + await retry.waitFor('updates', async () => { + return ( + (await PageObjects.unifiedFieldList.getSidebarAriaDescription()) === + '4 available fields. 0 meta fields.' + ); + }); + + expect( + (await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('available')).join(', ') + ).to.be( + 'relatedContent.article:modified_time, relatedContent.article:published_time, relatedContent.article:section, relatedContent.article:tag' + ); + }); + it('should ignore empty search', async function () { await PageObjects.unifiedFieldList.findFieldByName(' '); // only spaces diff --git a/test/functional/apps/discover/group6/index.ts b/test/functional/apps/discover/group6/index.ts index f71d96e63d2fd..6ed514463705f 100644 --- a/test/functional/apps/discover/group6/index.ts +++ b/test/functional/apps/discover/group6/index.ts @@ -25,5 +25,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_time_field_column')); loadTestFile(require.resolve('./_unsaved_changes_badge')); loadTestFile(require.resolve('./_view_mode_toggle')); + loadTestFile(require.resolve('./_field_stats_table')); }); } diff --git a/test/functional/services/dashboard/add_panel.ts b/test/functional/services/dashboard/add_panel.ts index ffc62bdfdb68a..16b283f2b5c53 100644 --- a/test/functional/services/dashboard/add_panel.ts +++ b/test/functional/services/dashboard/add_panel.ts @@ -69,9 +69,14 @@ export class DashboardAddPanelService extends FtrService { await this.testSubjects.click(`visType-${visType}`); } - async verifyEmbeddableFactoryGroupExists(groupId: string) { + async verifyEmbeddableFactoryGroupExists(groupId: string, expectExist: boolean = true) { this.log.debug('DashboardAddPanel.verifyEmbeddableFactoryGroupExists'); - await this.testSubjects.existOrFail(`dashboardEditorMenu-${groupId}Group`); + const testSubject = `dashboardEditorMenu-${groupId}Group`; + if (expectExist) { + await this.testSubjects.existOrFail(testSubject); + } else { + await this.testSubjects.missingOrFail(testSubject); + } } async clickAddNewEmbeddableLink(type: string) { diff --git a/tsconfig.base.json b/tsconfig.base.json index 85f84a2609046..6d4637281115e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -808,6 +808,8 @@ "@kbn/eso-model-version-example/*": ["examples/eso_model_version_example/*"], "@kbn/eso-plugin": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin"], "@kbn/eso-plugin/*": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin/*"], + "@kbn/esql": ["src/plugins/esql"], + "@kbn/esql/*": ["src/plugins/esql/*"], "@kbn/esql-ast": ["packages/kbn-esql-ast"], "@kbn/esql-ast/*": ["packages/kbn-esql-ast/*"], "@kbn/esql-ast-inspector-plugin": ["examples/esql_ast_inspector"], @@ -1310,6 +1312,8 @@ "@kbn/react-kibana-context-theme/*": ["packages/react/kibana_context/theme/*"], "@kbn/react-kibana-mount": ["packages/react/kibana_mount"], "@kbn/react-kibana-mount/*": ["packages/react/kibana_mount/*"], + "@kbn/recently-accessed": ["packages/kbn-recently-accessed"], + "@kbn/recently-accessed/*": ["packages/kbn-recently-accessed/*"], "@kbn/remote-clusters-plugin": ["x-pack/plugins/remote_clusters"], "@kbn/remote-clusters-plugin/*": ["x-pack/plugins/remote_clusters/*"], "@kbn/rendering-plugin": ["test/plugin_functional/plugins/rendering_plugin"], @@ -1724,8 +1728,6 @@ "@kbn/testing-embedded-lens-plugin/*": ["x-pack/examples/testing_embedded_lens/*"], "@kbn/text-based-editor": ["packages/kbn-text-based-editor"], "@kbn/text-based-editor/*": ["packages/kbn-text-based-editor/*"], - "@kbn/text-based-languages": ["src/plugins/text_based_languages"], - "@kbn/text-based-languages/*": ["src/plugins/text_based_languages/*"], "@kbn/third-party-lens-navigation-prompt-plugin": ["x-pack/examples/third_party_lens_navigation_prompt"], "@kbn/third-party-lens-navigation-prompt-plugin/*": ["x-pack/examples/third_party_lens_navigation_prompt/*"], "@kbn/third-party-vis-lens-example-plugin": ["x-pack/examples/third_party_vis_lens_example"], diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/mock/get_anonymized_value/index.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/mock/get_anonymized_value/index.ts index 3822c736b670e..256f9776c4563 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/mock/get_anonymized_value/index.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/mock/get_anonymized_value/index.ts @@ -5,13 +5,6 @@ * 2.0. */ -import { Replacements } from '../../schemas'; - /** This mock returns the reverse of `value` */ -export const mockGetAnonymizedValue = ({ - currentReplacements, - rawValue, -}: { - currentReplacements: Replacements | undefined; - rawValue: string; -}): string => rawValue.split('').reverse().join(''); +export const mockGetAnonymizedValue = ({ rawValue }: { rawValue: string }): string => + rawValue.split('').reverse().join(''); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/assistant_header_flyout.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/assistant_header_flyout.tsx deleted file mode 100644 index 5725d983eff33..0000000000000 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/assistant_header_flyout.tsx +++ /dev/null @@ -1,284 +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 React, { useState, useMemo, useCallback } from 'react'; -import { QueryObserverResult, RefetchOptions, RefetchQueryFilters } from '@tanstack/react-query'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPopover, - EuiContextMenu, - EuiButtonIcon, - EuiPanel, - EuiConfirmModal, - EuiToolTip, -} from '@elastic/eui'; -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { DocLinksStart } from '@kbn/core-doc-links-browser'; -import { isEmpty } from 'lodash'; -import { Conversation } from '../../..'; -import { AssistantTitle } from '../assistant_title'; -import { ConnectorSelectorInline } from '../../connectorland/connector_selector_inline/connector_selector_inline'; -import { FlyoutNavigation } from '../assistant_overlay/flyout_navigation'; -import { AssistantSettingsButton } from '../settings/assistant_settings_button'; -import * as i18n from './translations'; -import { AIConnector } from '../../connectorland/connector_selector'; - -interface OwnProps { - selectedConversation: Conversation | undefined; - defaultConnector?: AIConnector; - docLinks: Omit; - isDisabled: boolean; - isSettingsModalVisible: boolean; - onToggleShowAnonymizedValues: () => void; - setIsSettingsModalVisible: React.Dispatch>; - showAnonymizedValues: boolean; - onChatCleared: () => void; - onCloseFlyout?: () => void; - chatHistoryVisible?: boolean; - setChatHistoryVisible?: React.Dispatch>; - onConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => void; - conversations: Record; - conversationsLoaded: boolean; - refetchConversationsState: () => Promise; - onConversationCreate: () => Promise; - isAssistantEnabled: boolean; - refetchPrompts?: ( - options?: RefetchOptions & RefetchQueryFilters - ) => Promise>; -} - -type Props = OwnProps; -/** - * Renders the header of the Elastic AI Assistant. - * Provide a user interface for selecting and managing conversations, - * toggling the display of anonymized values, and accessing the assistant settings. - */ -export const AssistantHeaderFlyout: React.FC = ({ - selectedConversation, - defaultConnector, - docLinks, - isDisabled, - isSettingsModalVisible, - onToggleShowAnonymizedValues, - setIsSettingsModalVisible, - showAnonymizedValues, - onChatCleared, - chatHistoryVisible, - setChatHistoryVisible, - onCloseFlyout, - onConversationSelected, - conversations, - conversationsLoaded, - refetchConversationsState, - onConversationCreate, - isAssistantEnabled, - refetchPrompts, -}) => { - const showAnonymizedValuesChecked = useMemo( - () => - selectedConversation?.replacements != null && - Object.keys(selectedConversation?.replacements).length > 0 && - showAnonymizedValues, - [selectedConversation?.replacements, showAnonymizedValues] - ); - - const selectedConnectorId = useMemo( - () => selectedConversation?.apiConfig?.connectorId, - [selectedConversation?.apiConfig?.connectorId] - ); - - const [isPopoverOpen, setPopover] = useState(false); - - const onButtonClick = useCallback(() => { - setPopover(!isPopoverOpen); - }, [isPopoverOpen]); - - const closePopover = useCallback(() => { - setPopover(false); - }, []); - - const [isResetConversationModalVisible, setIsResetConversationModalVisible] = useState(false); - - const closeDestroyModal = useCallback(() => setIsResetConversationModalVisible(false), []); - const showDestroyModal = useCallback(() => setIsResetConversationModalVisible(true), []); - - const onConversationChange = useCallback( - (updatedConversation) => { - onConversationSelected({ - cId: updatedConversation.id, - cTitle: updatedConversation.title, - }); - }, - [onConversationSelected] - ); - - const panels = useMemo( - () => [ - { - id: 0, - items: [ - { - name: i18n.RESET_CONVERSATION, - css: css` - color: ${euiThemeVars.euiColorDanger}; - `, - onClick: showDestroyModal, - icon: 'refresh', - 'data-test-subj': 'clear-chat', - }, - ], - }, - ], - [showDestroyModal] - ); - - const handleReset = useCallback(() => { - onChatCleared(); - closeDestroyModal(); - closePopover(); - }, [onChatCleared, closeDestroyModal, closePopover]); - - return ( - <> - - - - - - - {onCloseFlyout && ( - - - - )} - - - - - - - - - - - - - - - - - - - - - } - isOpen={isPopoverOpen} - closePopover={closePopover} - panelPaddingSize="none" - anchorPosition="downLeft" - > - - - - - - - - {isResetConversationModalVisible && ( - -

{i18n.CLEAR_CHAT_CONFIRMATION}

-
- )} - - ); -}; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx index f806f5d1ef7c6..b4f4bd2c25384 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx @@ -20,7 +20,7 @@ const mockConversations = { }; const testProps = { conversationsLoaded: true, - currentConversation: welcomeConvo, + selectedConversation: welcomeConvo, title: 'Test Title', docLinks: { ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', @@ -30,12 +30,13 @@ const testProps = { isSettingsModalVisible: false, onConversationSelected, onToggleShowAnonymizedValues: jest.fn(), - selectedConversationId: emptyWelcomeConvo.id, setIsSettingsModalVisible: jest.fn(), - onConversationDeleted: jest.fn(), + onConversationCreate: jest.fn(), + onChatCleared: jest.fn(), showAnonymizedValues: false, conversations: mockConversations, refetchConversationsState: jest.fn(), + isAssistantEnabled: true, anonymizationFields: { total: 0, page: 1, perPage: 1000, data: [] }, refetchAnonymizationFieldsResults: jest.fn(), allPrompts: [], @@ -69,53 +70,64 @@ describe('AssistantHeader', () => { beforeEach(() => { jest.clearAllMocks(); }); - it('showAnonymizedValues is not checked when currentConversation.replacements is null', () => { + it('showAnonymizedValues is not checked when selectedConversation.replacements is null', () => { const { getByText, getByTestId } = render(, { wrapper: TestProviders, }); - expect(getByText('Test Title')).toBeInTheDocument(); - expect(getByTestId('showAnonymizedValues')).toHaveAttribute('aria-checked', 'false'); + expect(getByText(welcomeConvo.title)).toBeInTheDocument(); + expect(getByTestId('showAnonymizedValues').firstChild).toHaveAttribute( + 'data-euiicon-type', + 'eyeClosed' + ); }); - it('showAnonymizedValues is not checked when currentConversation.replacements is empty', () => { + it('showAnonymizedValues is not checked when selectedConversation.replacements is empty', () => { const { getByText, getByTestId } = render( , { wrapper: TestProviders, } ); - expect(getByText('Test Title')).toBeInTheDocument(); - expect(getByTestId('showAnonymizedValues')).toHaveAttribute('aria-checked', 'false'); + expect(getByText(welcomeConvo.title)).toBeInTheDocument(); + expect(getByTestId('showAnonymizedValues').firstChild).toHaveAttribute( + 'data-euiicon-type', + 'eyeClosed' + ); }); - it('showAnonymizedValues is not checked when currentConversation.replacements has values and showAnonymizedValues is false', () => { + it('showAnonymizedValues is not checked when selectedConversation.replacements has values and showAnonymizedValues is false', () => { const { getByTestId } = render( - , + , { wrapper: TestProviders, } ); - expect(getByTestId('showAnonymizedValues')).toHaveAttribute('aria-checked', 'false'); + expect(getByTestId('showAnonymizedValues').firstChild).toHaveAttribute( + 'data-euiicon-type', + 'eyeClosed' + ); }); - it('showAnonymizedValues is checked when currentConversation.replacements has values and showAnonymizedValues is true', () => { + it('showAnonymizedValues is checked when selectedConversation.replacements has values and showAnonymizedValues is true', () => { const { getByTestId } = render( - , + , { wrapper: TestProviders, } ); - expect(getByTestId('showAnonymizedValues')).toHaveAttribute('aria-checked', 'true'); + expect(getByTestId('showAnonymizedValues').firstChild).toHaveAttribute( + 'data-euiicon-type', + 'eye' + ); }); it('Conversation is updated when connector change occurs', async () => { const { getByTestId } = render(, { wrapper: TestProviders, }); - fireEvent.click(getByTestId('connectorSelectorPlaceholderButton')); fireEvent.click(getByTestId('connector-selector')); await act(async () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx index 7507c14648614..30e620ea38873 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx @@ -5,44 +5,47 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useState, useMemo, useCallback } from 'react'; +import { QueryObserverResult, RefetchOptions, RefetchQueryFilters } from '@tanstack/react-query'; import { EuiFlexGroup, EuiFlexItem, - EuiHorizontalRule, - EuiSpacer, - EuiSwitch, + EuiPopover, + EuiContextMenu, + EuiButtonIcon, + EuiPanel, + EuiConfirmModal, EuiToolTip, } from '@elastic/eui'; -import { QueryObserverResult, RefetchOptions, RefetchQueryFilters } from '@tanstack/react-query'; import { css } from '@emotion/react'; -import { DocLinksStart } from '@kbn/core-doc-links-browser'; +import { euiThemeVars } from '@kbn/ui-theme'; import { isEmpty } from 'lodash'; -import { PromptResponse } from '@kbn/elastic-assistant-common'; -import { AIConnector } from '../../connectorland/connector_selector'; import { Conversation } from '../../..'; import { AssistantTitle } from '../assistant_title'; -import { ConversationSelector } from '../conversations/conversation_selector'; +import { ConnectorSelectorInline } from '../../connectorland/connector_selector_inline/connector_selector_inline'; +import { FlyoutNavigation } from '../assistant_overlay/flyout_navigation'; import { AssistantSettingsButton } from '../settings/assistant_settings_button'; import * as i18n from './translations'; +import { AIConnector } from '../../connectorland/connector_selector'; interface OwnProps { - currentConversation?: Conversation; + selectedConversation: Conversation | undefined; defaultConnector?: AIConnector; - docLinks: Omit; isDisabled: boolean; isSettingsModalVisible: boolean; - onConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => void; - onConversationDeleted: (conversationId: string) => void; onToggleShowAnonymizedValues: () => void; setIsSettingsModalVisible: React.Dispatch>; - shouldDisableKeyboardShortcut?: () => boolean; showAnonymizedValues: boolean; - title: string; + onChatCleared: () => void; + onCloseFlyout?: () => void; + chatHistoryVisible?: boolean; + setChatHistoryVisible?: React.Dispatch>; + onConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => void; conversations: Record; conversationsLoaded: boolean; refetchConversationsState: () => Promise; - allPrompts: PromptResponse[]; + onConversationCreate: () => Promise; + isAssistantEnabled: boolean; refetchPrompts?: ( options?: RefetchOptions & RefetchQueryFilters ) => Promise>; @@ -55,31 +58,53 @@ type Props = OwnProps; * toggling the display of anonymized values, and accessing the assistant settings. */ export const AssistantHeader: React.FC = ({ - currentConversation, + selectedConversation, defaultConnector, - docLinks, isDisabled, isSettingsModalVisible, - onConversationSelected, - onConversationDeleted, onToggleShowAnonymizedValues, setIsSettingsModalVisible, - shouldDisableKeyboardShortcut, showAnonymizedValues, - title, + onChatCleared, + chatHistoryVisible, + setChatHistoryVisible, + onCloseFlyout, + onConversationSelected, conversations, conversationsLoaded, refetchConversationsState, - allPrompts, + onConversationCreate, + isAssistantEnabled, refetchPrompts, }) => { const showAnonymizedValuesChecked = useMemo( () => - currentConversation?.replacements != null && - Object.keys(currentConversation?.replacements).length > 0 && + selectedConversation?.replacements != null && + Object.keys(selectedConversation?.replacements).length > 0 && showAnonymizedValues, - [currentConversation?.replacements, showAnonymizedValues] + [selectedConversation?.replacements, showAnonymizedValues] ); + + const selectedConnectorId = useMemo( + () => selectedConversation?.apiConfig?.connectorId, + [selectedConversation?.apiConfig?.connectorId] + ); + + const [isPopoverOpen, setPopover] = useState(false); + + const onButtonClick = useCallback(() => { + setPopover(!isPopoverOpen); + }, [isPopoverOpen]); + + const closePopover = useCallback(() => { + setPopover(false); + }, []); + + const [isResetConversationModalVisible, setIsResetConversationModalVisible] = useState(false); + + const closeDestroyModal = useCallback(() => setIsResetConversationModalVisible(false), []); + const showDestroyModal = useCallback(() => setIsResetConversationModalVisible(true), []); + const onConversationChange = useCallback( (updatedConversation) => { onConversationSelected({ @@ -89,90 +114,163 @@ export const AssistantHeader: React.FC = ({ }, [onConversationSelected] ); - const selectedConversationId = useMemo( - () => - !isEmpty(currentConversation?.id) ? currentConversation?.id : currentConversation?.title, - [currentConversation?.id, currentConversation?.title] + + const panels = useMemo( + () => [ + { + id: 0, + items: [ + { + name: i18n.RESET_CONVERSATION, + css: css` + color: ${euiThemeVars.euiColorDanger}; + `, + onClick: showDestroyModal, + icon: 'refresh', + 'data-test-subj': 'clear-chat', + }, + ], + }, + ], + [showDestroyModal] ); + const handleReset = useCallback(() => { + onChatCleared(); + closeDestroyModal(); + closePopover(); + }, [onChatCleared, closeDestroyModal, closePopover]); + return ( <> - + + + + + + {onCloseFlyout && ( + + + + )} + + + - - - - - - + + + + - <> - - + + + + + - - - - + + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + - - - - + + + + {isResetConversationModalVisible && ( + +

{i18n.CLEAR_CHAT_CONFIRMATION}

+
+ )} ); }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/flyout_navigation.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/flyout_navigation.tsx index 85d7360c2870a..3f7c3f7ea1bcb 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/flyout_navigation.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/flyout_navigation.tsx @@ -48,6 +48,7 @@ export const FlyoutNavigation = memo( onClick={onToggle} iconType={isExpanded ? 'arrowEnd' : 'arrowStart'} size="xs" + data-test-subj="aiAssistantFlyoutNavigationToggle" aria-label={ isExpanded ? i18n.translate( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx index 679901bc02748..9e6a9164607a3 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx @@ -24,31 +24,33 @@ describe('AssistantOverlay', () => { it('renders when isAssistantEnabled prop is true and keyboard shortcut is pressed', () => { const { getByTestId } = render( - + ); fireEvent.keyDown(document, { key: ';', ctrlKey: true }); - const modal = getByTestId('ai-assistant-modal'); - expect(modal).toBeInTheDocument(); + const flyout = getByTestId('ai-assistant-flyout'); + expect(flyout).toBeInTheDocument(); }); - it('modal closes when close button is clicked', () => { - const { getByLabelText, queryByTestId } = render( + it('flyout closes when close button is clicked', () => { + const { queryByTestId } = render( - + ); fireEvent.keyDown(document, { key: ';', ctrlKey: true }); - const closeButton = getByLabelText('Closes this modal window'); - fireEvent.click(closeButton); - const modal = queryByTestId('ai-assistant-modal'); - expect(modal).not.toBeInTheDocument(); + const closeButton = queryByTestId('euiFlyoutCloseButton'); + if (closeButton) { + fireEvent.click(closeButton); + } + const flyout = queryByTestId('ai-assistant-flyout'); + expect(flyout).not.toBeInTheDocument(); }); - it('Assistant invoked from shortcut tracking happens on modal open only (not close)', () => { + it('Assistant invoked from shortcut tracking happens on flyout open only (not close)', () => { render( - + ); fireEvent.keyDown(document, { key: ';', ctrlKey: true }); @@ -61,26 +63,26 @@ describe('AssistantOverlay', () => { expect(reportAssistantInvoked).toHaveBeenCalledTimes(1); }); - it('modal closes when shortcut is pressed and modal is already open', () => { + it('flyout closes when shortcut is pressed and flyout is already open', () => { const { queryByTestId } = render( - + ); fireEvent.keyDown(document, { key: ';', ctrlKey: true }); fireEvent.keyDown(document, { key: ';', ctrlKey: true }); - const modal = queryByTestId('ai-assistant-modal'); - expect(modal).not.toBeInTheDocument(); + const flyout = queryByTestId('ai-assistant-flyout'); + expect(flyout).not.toBeInTheDocument(); }); - it('modal does not open when incorrect shortcut is pressed', () => { + it('flyout does not open when incorrect shortcut is pressed', () => { const { queryByTestId } = render( - + ); fireEvent.keyDown(document, { key: 'a', ctrlKey: true }); - const modal = queryByTestId('ai-assistant-modal'); - expect(modal).not.toBeInTheDocument(); + const flyout = queryByTestId('ai-assistant-flyout'); + expect(flyout).not.toBeInTheDocument(); }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx index 44907d8b1fd00..689f60f0a52d9 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx @@ -6,12 +6,12 @@ */ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { EuiModal, EuiFlyoutResizable, useEuiTheme } from '@elastic/eui'; +import { EuiFlyoutResizable } from '@elastic/eui'; import useEvent from 'react-use/lib/useEvent'; -// eslint-disable-next-line @kbn/eslint/module_migration -import styled from 'styled-components'; import { css } from '@emotion/react'; +// eslint-disable-next-line @kbn/eslint/module_migration +import { createGlobalStyle } from 'styled-components'; import { ShowAssistantOverlayProps, useAssistantContext, @@ -22,23 +22,21 @@ import { WELCOME_CONVERSATION_TITLE } from '../use_conversation/translations'; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; -const StyledEuiModal = styled(EuiModal)` - ${({ theme }) => `margin-top: ${theme.eui.euiSizeXXL};`} - min-width: 95vw; - min-height: 25vh; -`; - /** * Modal container for Elastic AI Assistant conversations, receiving the page contents as context, plus whatever * component currently has focus and any specific context it may provide through the SAssInterface. */ export interface Props { - isFlyoutMode: boolean; currentUserAvatar?: UserAvatar; } -export const AssistantOverlay = React.memo(({ isFlyoutMode, currentUserAvatar }) => { - const { euiTheme } = useEuiTheme(); +export const UnifiedTimelineGlobalStyles = createGlobalStyle` + body:has(.timeline-portal-overlay-mask) .euiOverlayMask { + z-index: 1003 !important; + } +`; + +export const AssistantOverlay = React.memo(({ currentUserAvatar }) => { const [isModalVisible, setIsModalVisible] = useState(false); const [conversationTitle, setConversationTitle] = useState( WELCOME_CONVERSATION_TITLE @@ -130,8 +128,8 @@ export const AssistantOverlay = React.memo(({ isFlyoutMode, currentUserAv if (!isModalVisible) return null; - if (isFlyoutMode) { - return ( + return ( + <> (({ isFlyoutMode, currentUserAv data-test-subj="ai-assistant-flyout" paddingSize="none" hideCloseButton - // EUI TODO: This z-index override of EuiOverlayMask is a workaround, and ideally should be resolved with a cleaner UI/UX flow long-term - maskProps={{ style: `z-index: ${(euiTheme.levels.flyout as number) + 3}` }} // we need this flyout to be above the timeline flyout (which has a z-index of 1002) > - ); - } - - return ( - <> - {isModalVisible && ( - - - - )} + ); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.test.tsx index ee4a998a1439f..d9dd84cb0b51d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { AssistantTitle } from '.'; import { TestProviders } from '../../mock/test_providers/test_providers'; @@ -14,7 +14,6 @@ const testProps = { title: 'Test Title', docLinks: { ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', DOC_LINK_VERSION: '7.15' }, selectedConversation: undefined, - isFlyoutMode: false, onChange: jest.fn(), refetchConversationsState: jest.fn(), }; @@ -28,22 +27,4 @@ describe('AssistantTitle', () => { ); expect(getByText('Test Title')).toBeInTheDocument(); }); - - it('clicking on the popover button opens the popover with the correct link', () => { - const { getByTestId, queryByTestId } = render( - - - , - { - wrapper: TestProviders, - } - ); - expect(queryByTestId('tooltipContent')).not.toBeInTheDocument(); - fireEvent.click(getByTestId('tooltipIcon')); - expect(getByTestId('tooltipContent')).toBeInTheDocument(); - expect(getByTestId('externalDocumentationLink')).toHaveAttribute( - 'href', - 'https://www.elastic.co/guide/en/security/7.15/security-assistant.html' - ); - }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx index 7e9934afcaa90..2090a92645c65 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_title/index.tsx @@ -5,24 +5,10 @@ * 2.0. */ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { - EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, - EuiInlineEditTitle, - EuiLink, - EuiModalHeaderTitle, - EuiPopover, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import type { DocLinksStart } from '@kbn/core-doc-links-browser'; -import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useCallback, useEffect, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiInlineEditTitle } from '@elastic/eui'; import { css } from '@emotion/react'; -import * as i18n from '../translations'; import type { Conversation } from '../../..'; -import { ConnectorSelectorInline } from '../../connectorland/connector_selector_inline/connector_selector_inline'; import { AssistantAvatar } from '../assistant_avatar/assistant_avatar'; import { useConversation } from '../use_conversation'; import { NEW_CHAT } from '../conversations/conversation_sidepanel/translations'; @@ -32,63 +18,14 @@ import { NEW_CHAT } from '../conversations/conversation_sidepanel/translations'; * information about the assistant feature and access to documentation. */ export const AssistantTitle: React.FC<{ - isDisabled?: boolean; title?: string; - docLinks: Omit; selectedConversation: Conversation | undefined; - isFlyoutMode: boolean; - onChange: (updatedConversation: Conversation) => void; refetchConversationsState: () => Promise; -}> = ({ - isDisabled = false, - title, - docLinks, - selectedConversation, - isFlyoutMode, - onChange, - refetchConversationsState, -}) => { +}> = ({ title, selectedConversation, refetchConversationsState }) => { const [newTitle, setNewTitle] = useState(title); const [newTitleError, setNewTitleError] = useState(false); const { updateConversationTitle } = useConversation(); - const selectedConnectorId = selectedConversation?.apiConfig?.connectorId; - - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; - const url = `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/security-assistant.html`; - - const documentationLink = useMemo( - () => ( - - {i18n.DOCUMENTATION} - - ), - [url] - ); - - const content = useMemo( - () => ( - - ), - [documentationLink] - ); - - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const onButtonClick = useCallback(() => setIsPopoverOpen((isOpen: boolean) => !isOpen), []); - const closePopover = useCallback(() => setIsPopoverOpen(false), []); - const handleUpdateTitle = useCallback( async (updatedTitle: string) => { setNewTitleError(false); @@ -109,108 +46,33 @@ export const AssistantTitle: React.FC<{ setNewTitle(title); }, [title]); - if (isFlyoutMode) { - return ( - - - - - - setNewTitle(e.currentTarget.nodeValue || '')} - onCancel={() => setNewTitle(title)} - onSave={handleUpdateTitle} - editModeProps={{ - formRowProps: { - fullWidth: true, - }, - }} - /> - - - ); - } - return ( - - - - - - - - - - - -

{title}

-
-
- - - } - isOpen={isPopoverOpen} - closePopover={closePopover} - anchorPosition="rightUp" - > - - -

{content}

-
-
-
-
-
-
- {!isFlyoutMode && ( - - - - )} -
-
-
-
+ + + + + + setNewTitle(e.currentTarget.nodeValue || '')} + onCancel={() => setNewTitle(title)} + onSave={handleUpdateTitle} + editModeProps={{ + formRowProps: { + fullWidth: true, + }, + }} + /> + + ); }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.test.tsx index 36936c7565112..7fbd7e1a03366 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.test.tsx @@ -9,14 +9,11 @@ import React from 'react'; import { render, fireEvent, within } from '@testing-library/react'; import { ChatActions } from '.'; -const onChatCleared = jest.fn(); const onSendMessage = jest.fn(); const testProps = { isDisabled: false, isLoading: false, - onChatCleared, onSendMessage, - isFlyoutMode: false, promptValue: 'prompt', }; @@ -26,16 +23,9 @@ describe('ChatActions', () => { }); it('the component renders with all props', () => { const { getByTestId } = render(); - expect(getByTestId('clear-chat')).toHaveAttribute('aria-label', 'Clear chat'); expect(getByTestId('submit-chat')).toHaveAttribute('aria-label', 'Submit message'); }); - it('onChatCleared function is called when clear chat button is clicked', () => { - const { getByTestId } = render(); - fireEvent.click(getByTestId('clear-chat')); - expect(onChatCleared).toHaveBeenCalled(); - }); - it('onSendMessage function is called when send message button is clicked', () => { const { getByTestId } = render(); @@ -49,7 +39,6 @@ describe('ChatActions', () => { isDisabled: true, }; const { getByTestId } = render(); - expect(getByTestId('clear-chat')).toBeDisabled(); expect(getByTestId('submit-chat')).toBeDisabled(); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.tsx index e7ff0922b30ae..ba980356351fd 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_actions/index.tsx @@ -7,14 +7,12 @@ import React, { useCallback, useRef } from 'react'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; -import { CLEAR_CHAT, SUBMIT_MESSAGE } from '../translations'; +import { SUBMIT_MESSAGE } from '../translations'; interface OwnProps { isDisabled: boolean; isLoading: boolean; - isFlyoutMode: boolean; promptValue?: string; - onChatCleared: () => void; onSendMessage: () => void; } @@ -26,9 +24,7 @@ type Props = OwnProps; export const ChatActions: React.FC = ({ isDisabled, isLoading, - onChatCleared, onSendMessage, - isFlyoutMode, promptValue, }) => { const submitTooltipRef = useRef(null); @@ -39,21 +35,6 @@ export const ChatActions: React.FC = ({ return ( - {!isFlyoutMode && ( - - - - - - )} = ({ aria-label={SUBMIT_MESSAGE} data-test-subj="submit-chat" color="primary" - display={isFlyoutMode && promptValue?.length ? 'fill' : 'base'} - size={isFlyoutMode ? 'm' : 'xs'} - iconType={isFlyoutMode ? 'kqlFunction' : 'returnKey'} + display={promptValue?.length ? 'fill' : 'base'} + size={'m'} + iconType={'kqlFunction'} isDisabled={isDisabled || !promptValue?.length} isLoading={isLoading} onClick={onSendMessage} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.test.tsx index ab7b942476f81..99f30cde68a82 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.test.tsx @@ -12,12 +12,10 @@ import { TestProviders } from '../../mock/test_providers/test_providers'; jest.mock('./use_chat_send'); -const handleOnChatCleared = jest.fn(); const handlePromptChange = jest.fn(); const handleSendMessage = jest.fn(); const handleRegenerateResponse = jest.fn(); const testProps: Props = { - handleOnChatCleared, handlePromptChange, handleSendMessage, handleRegenerateResponse, @@ -25,7 +23,6 @@ const testProps: Props = { isDisabled: false, shouldRefocusPrompt: false, userPrompt: '', - isFlyoutMode: false, }; describe('ChatSend', () => { beforeEach(() => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx index 880d4d5f9f88f..c292a70252a03 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx @@ -14,11 +14,10 @@ import { ChatActions } from '../chat_actions'; import { PromptTextArea } from '../prompt_textarea'; import { useAutosizeTextArea } from './use_autosize_textarea'; -export interface Props extends Omit { +export interface Props extends Omit { isDisabled: boolean; shouldRefocusPrompt: boolean; userPrompt: string | null; - isFlyoutMode: boolean; } /** @@ -26,12 +25,10 @@ export interface Props extends Omit { * Allows the user to clear the chat and switch between different system prompts. */ export const ChatSend: React.FC = ({ - handleOnChatCleared, handlePromptChange, handleSendMessage, isDisabled, isLoading, - isFlyoutMode, shouldRefocusPrompt, userPrompt, }) => { @@ -58,7 +55,7 @@ export const ChatSend: React.FC = ({ return ( = ({ handlePromptChange={handlePromptChange} value={promptValue} isDisabled={isDisabled} - isFlyoutMode={isFlyoutMode} /> diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx index 17a421313e3a4..a9231499570c7 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.test.tsx @@ -21,7 +21,6 @@ jest.mock('../use_conversation'); jest.mock('../../..'); const setEditingSystemPromptId = jest.fn(); -const setPromptTextPreview = jest.fn(); const setSelectedPromptContexts = jest.fn(); const setUserPrompt = jest.fn(); const sendMessage = jest.fn(); @@ -43,7 +42,6 @@ export const testProps: UseChatSendProps = { } as unknown as HttpSetup, editingSystemPromptId: defaultSystemPrompt.id, setEditingSystemPromptId, - setPromptTextPreview, setSelectedPromptContexts, setUserPrompt, setCurrentConversation, @@ -75,7 +73,6 @@ describe('use chat send', () => { }); result.current.handleOnChatCleared(); expect(clearConversation).toHaveBeenCalled(); - expect(setPromptTextPreview).toHaveBeenCalledWith(''); expect(setUserPrompt).toHaveBeenCalledWith(''); expect(setSelectedPromptContexts).toHaveBeenCalledWith({}); await waitFor(() => { @@ -89,7 +86,6 @@ describe('use chat send', () => { wrapper: TestProviders, }); result.current.handlePromptChange('new prompt'); - expect(setPromptTextPreview).toHaveBeenCalledWith('new prompt'); expect(setUserPrompt).toHaveBeenCalledWith('new prompt'); }); it('handleSendMessage sends message with context prompt when a valid prompt text is provided', async () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx index 9d5e822fcdf55..5a70b6ad32cd8 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx @@ -25,7 +25,6 @@ export interface UseChatSendProps { http: HttpSetup; selectedPromptContexts: Record; setEditingSystemPromptId: React.Dispatch>; - setPromptTextPreview: React.Dispatch>; setSelectedPromptContexts: React.Dispatch< React.SetStateAction> >; @@ -54,7 +53,6 @@ export const useChatSend = ({ http, selectedPromptContexts, setEditingSystemPromptId, - setPromptTextPreview, setSelectedPromptContexts, setUserPrompt, setCurrentConversation, @@ -69,7 +67,6 @@ export const useChatSend = ({ const { clearConversation, removeLastMessage } = useConversation(); const handlePromptChange = (prompt: string) => { - setPromptTextPreview(prompt); setUserPrompt(prompt); }; @@ -120,7 +117,6 @@ export const useChatSend = ({ // Reset prompt context selection and preview before sending: setSelectedPromptContexts({}); - setPromptTextPreview(''); const rawResponse = await sendMessage({ apiConfig: currentConversation.apiConfig, @@ -168,7 +164,6 @@ export const useChatSend = ({ selectedPromptContexts, sendMessage, setCurrentConversation, - setPromptTextPreview, setSelectedPromptContexts, toasts, ] @@ -214,7 +209,6 @@ export const useChatSend = ({ conversation: currentConversation, })?.id; - setPromptTextPreview(''); setUserPrompt(''); setSelectedPromptContexts({}); if (currentConversation) { @@ -230,7 +224,6 @@ export const useChatSend = ({ currentConversation, setCurrentConversation, setEditingSystemPromptId, - setPromptTextPreview, setSelectedPromptContexts, setUserPrompt, ]); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.test.tsx index 0168c27c7f548..da2dd3008a1b0 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.test.tsx @@ -33,7 +33,6 @@ const mockPromptContexts: Record = { const defaultProps = { anonymizationFields: { total: 0, page: 1, perPage: 1000, data: [] }, promptContexts: mockPromptContexts, - isFlyoutMode: false, }; describe('ContextPills', () => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.tsx index ce5a0cf59ca6a..d3ae29643804e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/context_pills/index.tsx @@ -5,20 +5,14 @@ * 2.0. */ -import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { sortBy } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; -// eslint-disable-next-line @kbn/eslint/module_migration -import styled from 'styled-components'; import { FindAnonymizationFieldsResponse } from '@kbn/elastic-assistant-common/impl/schemas/anonymization_fields/find_anonymization_fields_route.gen'; import { getNewSelectedPromptContext } from '../../data_anonymization/get_new_selected_prompt_context'; import type { PromptContext, SelectedPromptContext } from '../prompt_context/types'; -const PillButton = styled(EuiButton)` - margin-right: ${({ theme }) => theme.eui.euiSizeXS}; -`; - interface Props { anonymizationFields: FindAnonymizationFieldsResponse; promptContexts: Record; @@ -26,7 +20,6 @@ interface Props { setSelectedPromptContexts: React.Dispatch< React.SetStateAction> >; - isFlyoutMode: boolean; } const ContextPillsComponent: React.FC = ({ @@ -34,7 +27,6 @@ const ContextPillsComponent: React.FC = ({ promptContexts, selectedPromptContexts, setSelectedPromptContexts, - isFlyoutMode, }) => { const sortedPromptContexts = useMemo( () => sortBy('description', Object.values(promptContexts)), @@ -63,7 +55,7 @@ const ContextPillsComponent: React.FC = ({ {sortedPromptContexts.map(({ description, id, tooltip }) => { // Workaround for known issue where tooltip won't dismiss after button state is changed once clicked // See: https://github.com/elastic/eui/issues/6488#issuecomment-1379656704 - const button = isFlyoutMode ? ( + const button = ( = ({ > {description} - ) : ( - selectPromptContext(id)} - > - {description} - ); return ( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx index fd9cddc39dbbe..4ee8076c42a9d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.tsx @@ -35,7 +35,6 @@ interface Props { selectedConversationId: string | undefined; onConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => void; onConversationDeleted: (conversationId: string) => void; - shouldDisableKeyboardShortcut?: () => boolean; isDisabled?: boolean; conversations: Record; allPrompts: PromptResponse[]; @@ -65,7 +64,6 @@ export const ConversationSelector: React.FC = React.memo( defaultConnector, onConversationSelected, onConversationDeleted, - shouldDisableKeyboardShortcut = () => false, isDisabled = false, conversations, allPrompts, @@ -199,9 +197,8 @@ export const ConversationSelector: React.FC = React.memo( const renderOption: ( option: ConversationSelectorOption, - searchValue: string, - OPTION_CONTENT_CLASSNAME: string - ) => React.ReactNode = (option, searchValue, contentClassName) => { + searchValue: string + ) => React.ReactNode = (option, searchValue) => { const { label, id, value } = option; return ( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx index f4b8f9a79412f..f1edb5a9dc2a9 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx @@ -27,7 +27,6 @@ interface Props { onConversationDeleted: (conversationTitle: string) => void; onConversationSelectionChange: (conversation?: Conversation | string) => void; selectedConversationTitle: string; - shouldDisableKeyboardShortcut?: () => boolean; isDisabled?: boolean; } @@ -62,7 +61,6 @@ export const ConversationSelectorSettings: React.FC = React.memo( onConversationSelectionChange, selectedConversationTitle, isDisabled, - shouldDisableKeyboardShortcut = () => false, }) => { const conversationTitles = useMemo( () => Object.values(conversations).map((c) => c.title), diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx index cba17030e1577..1584a46ee687a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.tsx @@ -49,7 +49,6 @@ export interface ConversationSettingsProps { React.SetStateAction >; isDisabled?: boolean; - isFlyoutMode: boolean; } /** @@ -66,7 +65,6 @@ export const ConversationSettings: React.FC = React.m conversationSettings, http, isDisabled = false, - isFlyoutMode, setAssistantStreamingEnabled, setConversationSettings, conversationsSettingsBulkActions, @@ -127,7 +125,6 @@ export const ConversationSettings: React.FC = React.m conversationsSettingsBulkActions={conversationsSettingsBulkActions} http={http} isDisabled={isDisabled} - isFlyoutMode={isFlyoutMode} selectedConversation={selectedConversationWithApiConfig} setConversationSettings={setConversationSettings} setConversationsSettingsBulkActions={setConversationsSettingsBulkActions} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx index 41da376d21b73..cf8275203090b 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings_editor.tsx @@ -31,7 +31,6 @@ export interface ConversationSettingsEditorProps { conversationsSettingsBulkActions: ConversationsBulkActions; http: HttpSetup; isDisabled?: boolean; - isFlyoutMode: boolean; selectedConversation?: Conversation; setConversationSettings: React.Dispatch>>; setConversationsSettingsBulkActions: React.Dispatch< @@ -49,7 +48,6 @@ export const ConversationSettingsEditor: React.FC @@ -304,7 +299,6 @@ export const ConversationSettingsEditor: React.FC diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx index 485f89358f57a..10608502e70d3 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings_management/index.tsx @@ -36,7 +36,6 @@ interface Props { defaultConnector?: AIConnector; handleSave: (shouldRefetchConversation?: boolean) => void; isDisabled?: boolean; - isFlyoutMode: boolean; onCancelClick: () => void; setAssistantStreamingEnabled: React.Dispatch>; setConversationSettings: React.Dispatch>>; @@ -62,7 +61,6 @@ const ConversationSettingsManagementComponent: React.FC = ({ conversationsLoaded, handleSave, isDisabled, - isFlyoutMode, onSelectedConversationChange, onCancelClick, selectedConversation, @@ -221,7 +219,6 @@ const ConversationSettingsManagementComponent: React.FC = ({ conversationsSettingsBulkActions={conversationsSettingsBulkActions} http={http} isDisabled={isDisabled} - isFlyoutMode={isFlyoutMode} selectedConversation={selectedConversation} setConversationSettings={setConversationSettings} setConversationsSettingsBulkActions={setConversationsSettingsBulkActions} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/title_field.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/title_field.tsx index acbda15320277..373c052ede6e1 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/title_field.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_sidepanel/title_field.tsx @@ -32,7 +32,7 @@ const TitleFieldComponent = ({ conversationIds, euiFieldProps }: TitleFieldProps ), value: true, }, - validate: (text: string) => { + validate: () => { if (conversationIds?.includes(value)) { return i18n.translate( 'xpack.elasticAssistant.conversationSidepanel.titleField.uniqueTitle', diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts index 19d703a271edc..b4ed11a82df9e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts @@ -22,12 +22,11 @@ const defaultConversation = { replacements: {}, title: 'conversation_id', }; -const isFlyoutMode = false; describe('helpers', () => { describe('isAssistantEnabled = false', () => { const isAssistantEnabled = false; it('When no conversation history, return only enterprise messaging', () => { - const result = getBlockBotConversation(defaultConversation, isAssistantEnabled, isFlyoutMode); + const result = getBlockBotConversation(defaultConversation, isAssistantEnabled); expect(result.messages).toEqual(enterpriseMessaging); expect(result.messages.length).toEqual(1); }); @@ -47,7 +46,7 @@ describe('helpers', () => { }, ], }; - const result = getBlockBotConversation(conversation, isAssistantEnabled, isFlyoutMode); + const result = getBlockBotConversation(conversation, isAssistantEnabled); expect(result.messages.length).toEqual(2); }); @@ -56,7 +55,7 @@ describe('helpers', () => { ...defaultConversation, messages: enterpriseMessaging, }; - const result = getBlockBotConversation(conversation, isAssistantEnabled, isFlyoutMode); + const result = getBlockBotConversation(conversation, isAssistantEnabled); expect(result.messages.length).toEqual(1); expect(result.messages).toEqual(enterpriseMessaging); }); @@ -77,7 +76,7 @@ describe('helpers', () => { }, ], }; - const result = getBlockBotConversation(conversation, isAssistantEnabled, isFlyoutMode); + const result = getBlockBotConversation(conversation, isAssistantEnabled); expect(result.messages.length).toEqual(3); }); }); @@ -85,8 +84,8 @@ describe('helpers', () => { describe('isAssistantEnabled = true', () => { const isAssistantEnabled = true; it('when no conversation history, returns the welcome conversation', () => { - const result = getBlockBotConversation(defaultConversation, isAssistantEnabled, isFlyoutMode); - expect(result.messages.length).toEqual(3); + const result = getBlockBotConversation(defaultConversation, isAssistantEnabled); + expect(result.messages.length).toEqual(0); }); it('returns a conversation history with the welcome conversation appended', () => { const conversation = { @@ -103,8 +102,8 @@ describe('helpers', () => { }, ], }; - const result = getBlockBotConversation(conversation, isAssistantEnabled, isFlyoutMode); - expect(result.messages.length).toEqual(4); + const result = getBlockBotConversation(conversation, isAssistantEnabled); + expect(result.messages.length).toEqual(1); }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts index e9a0599ca4fc2..f369bf430ea54 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts @@ -10,7 +10,7 @@ import { AIConnector } from '../connectorland/connector_selector'; import { FetchConnectorExecuteResponse, FetchConversationsResponse } from './api'; import { Conversation } from '../..'; import type { ClientMessage } from '../assistant_context/types'; -import { enterpriseMessaging, WELCOME_CONVERSATION } from './use_conversation/sample_conversations'; +import { enterpriseMessaging } from './use_conversation/sample_conversations'; export const getMessageFromRawResponse = ( rawResponse: FetchConnectorExecuteResponse @@ -57,8 +57,7 @@ export const mergeBaseWithPersistedConversations = ( export const getBlockBotConversation = ( conversation: Conversation, - isAssistantEnabled: boolean, - isFlyoutMode: boolean + isAssistantEnabled: boolean ): Conversation => { if (!isAssistantEnabled) { if ( @@ -76,7 +75,7 @@ export const getBlockBotConversation = ( return { ...conversation, - messages: [...conversation.messages, ...(!isFlyoutMode ? WELCOME_CONVERSATION.messages : [])], + messages: conversation.messages, }; }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx index b25945dd247bf..cd0d53bd460c3 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx @@ -7,12 +7,11 @@ import React from 'react'; -import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react'; +import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import { Assistant } from '.'; import type { IHttpFetchError } from '@kbn/core/public'; import { useLoadConnectors } from '../connectorland/use_load_connectors'; -import { useConnectorSetup } from '../connectorland/connector_setup'; import { DefinedUseQueryResult, UseQueryResult } from '@tanstack/react-query'; @@ -40,7 +39,7 @@ jest.mock('./use_conversation'); const renderAssistant = (extraProps = {}, providerProps = {}) => render( - + ); @@ -63,11 +62,12 @@ const mockData = { }, }; const mockDeleteConvo = jest.fn(); +const mockGetDefaultConversation = jest.fn().mockReturnValue(mockData.welcome_id); const clearConversation = jest.fn(); const mockUseConversation = { clearConversation: clearConversation.mockResolvedValue(mockData.welcome_id), getConversation: jest.fn(), - getDefaultConversation: jest.fn().mockReturnValue(mockData.welcome_id), + getDefaultConversation: mockGetDefaultConversation, deleteConversation: mockDeleteConvo, setApiConfig: jest.fn().mockResolvedValue({}), }; @@ -83,10 +83,6 @@ describe('Assistant', () => { persistToLocalStorage = jest.fn(); persistToSessionStorage = jest.fn(); (useConversation as jest.Mock).mockReturnValue(mockUseConversation); - jest.mocked(useConnectorSetup).mockReturnValue({ - comments: [], - prompt: <>, - }); jest.mocked(PromptEditor).mockReturnValue(null); jest.mocked(QuickPrompts).mockReturnValue(null); @@ -221,22 +217,21 @@ describe('Assistant', () => { it('should delete conversation when delete button is clicked', async () => { renderAssistant(); + const deleteButton = screen.getAllByTestId('delete-option')[0]; await act(async () => { - fireEvent.click( - within(screen.getByTestId('conversation-selector')).getByTestId( - 'comboBoxToggleListButton' - ) - ); + fireEvent.click(deleteButton); }); - const deleteButton = screen.getAllByTestId('delete-option')[0]; await act(async () => { - fireEvent.click(deleteButton); + fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); + }); + + await waitFor(() => { + expect(mockDeleteConvo).toHaveBeenCalledWith(mockData.electric_sheep_id.id); }); - expect(mockDeleteConvo).toHaveBeenCalledWith(mockData.welcome_id.id); }); it('should refetchConversationsState after clear chat history button click', async () => { - renderAssistant({ isFlyoutMode: true }); + renderAssistant(); fireEvent.click(screen.getByTestId('chat-context-menu')); fireEvent.click(screen.getByTestId('clear-chat')); fireEvent.click(screen.getByTestId('confirmModalConfirmButton')); @@ -259,7 +254,7 @@ describe('Assistant', () => { expect(persistToLocalStorage).toHaveBeenLastCalledWith(mockData.welcome_id.id); - const previousConversationButton = screen.getByLabelText('Previous conversation'); + const previousConversationButton = await screen.findByText(mockData.electric_sheep_id.title); expect(previousConversationButton).toBeInTheDocument(); await act(async () => { @@ -295,13 +290,13 @@ describe('Assistant', () => { isFetched: true, } as unknown as DefinedUseQueryResult, unknown>); - const { getByLabelText } = renderAssistant(); + const { findByText } = renderAssistant(); expect(persistToLocalStorage).toHaveBeenCalled(); expect(persistToLocalStorage).toHaveBeenLastCalledWith(mockData.welcome_id.id); - const previousConversationButton = getByLabelText('Previous conversation'); + const previousConversationButton = await findByText(mockData.electric_sheep_id.title); expect(previousConversationButton).toBeInTheDocument(); @@ -321,7 +316,7 @@ describe('Assistant', () => { renderAssistant({ setConversationTitle }); await act(async () => { - fireEvent.click(screen.getByLabelText('Previous conversation')); + fireEvent.click(await screen.findByText(mockData.electric_sheep_id.title)); }); expect(setConversationTitle).toHaveBeenLastCalledWith('electric sheep'); @@ -351,7 +346,7 @@ describe('Assistant', () => { } as unknown as DefinedUseQueryResult, unknown>); renderAssistant(); - const previousConversationButton = screen.getByLabelText('Previous conversation'); + const previousConversationButton = await screen.findByText('updated title'); await act(async () => { fireEvent.click(previousConversationButton); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx index 6892fdcaf48bd..3fe4e1586e239 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx @@ -5,8 +5,6 @@ * 2.0. */ -/* eslint-disable complexity */ - import React, { Dispatch, SetStateAction, @@ -26,9 +24,6 @@ import { EuiFlyoutFooter, EuiFlyoutHeader, EuiFlyoutBody, - EuiModalFooter, - EuiModalHeader, - EuiModalBody, EuiText, } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; @@ -43,7 +38,6 @@ import { PromptTypeEnum } from '@kbn/elastic-assistant-common/impl/schemas/promp import { useChatSend } from './chat_send/use_chat_send'; import { ChatSend } from './chat_send'; import { BlockBotCallToAction } from './block_bot/cta'; -import { AssistantHeader } from './assistant_header'; import { WELCOME_CONVERSATION_TITLE } from './use_conversation/translations'; import { getDefaultConnector, @@ -57,16 +51,15 @@ import { getNewSelectedPromptContext } from '../data_anonymization/get_new_selec import type { PromptContext, SelectedPromptContext } from './prompt_context/types'; import { useConversation } from './use_conversation'; import { CodeBlockDetails, getDefaultSystemPrompt } from './use_conversation/helpers'; -import { PromptEditor } from './prompt_editor'; import { QuickPrompts } from './quick_prompts/quick_prompts'; import { useLoadConnectors } from '../connectorland/use_load_connectors'; -import { useConnectorSetup } from '../connectorland/connector_setup'; +import { ConnectorSetup } from '../connectorland/connector_setup'; import { ConnectorMissingCallout } from '../connectorland/connector_missing_callout'; import { ConversationSidePanel } from './conversations/conversation_sidepanel'; import { NEW_CHAT } from './conversations/conversation_sidepanel/translations'; import { SystemPrompt } from './prompt_editor/system_prompt'; import { SelectedPromptContexts } from './prompt_editor/selected_prompt_contexts'; -import { AssistantHeaderFlyout } from './assistant_header/assistant_header_flyout'; +import { AssistantHeader } from './assistant_header'; import * as i18n from './translations'; export const CONVERSATION_SIDE_PANEL_WIDTH = 220; @@ -77,17 +70,12 @@ const CommentContainer = styled('span')` overflow: hidden; `; -const ModalPromptEditorWrapper = styled.div` - margin-right: 24px; -`; - import { FetchConversationsResponse, useFetchCurrentUserConversations, CONVERSATIONS_QUERY_KEYS, } from './api/conversations/use_fetch_current_user_conversations'; import { Conversation } from '../assistant_context/types'; -import { clearPresentationData } from '../connectorland/connector_setup/helpers'; import { getGenAiConfig } from '../connectorland/helpers'; import { AssistantAnimatedIcon } from './assistant_animated_icon'; import { useFetchAnonymizationFields } from './api/anonymization_fields/use_fetch_anonymization_fields'; @@ -102,7 +90,6 @@ export interface Props { showTitle?: boolean; setConversationTitle?: Dispatch>; onCloseFlyout?: () => void; - isFlyoutMode?: boolean; chatHistoryVisible?: boolean; setChatHistoryVisible?: Dispatch>; currentUserAvatar?: UserAvatar; @@ -120,7 +107,6 @@ const AssistantComponent: React.FC = ({ showTitle = true, setConversationTitle, onCloseFlyout, - isFlyoutMode = false, chatHistoryVisible, setChatHistoryVisible, currentUserAvatar, @@ -129,14 +115,12 @@ const AssistantComponent: React.FC = ({ assistantTelemetry, augmentMessageCodeBlocks, assistantAvailability: { isAssistantEnabled }, - docLinks, getComments, http, knowledgeBase: { isEnabledKnowledgeBase, isEnabledRAGAlerts }, promptContexts, setLastConversationId, getLastConversationId, - title, baseConversations, } = useAssistantContext(); @@ -251,7 +235,7 @@ const AssistantComponent: React.FC = ({ nextConversation?.id !== '' ? nextConversation?.id : nextConversation?.title ]) ?? conversations[WELCOME_CONVERSATION_TITLE] ?? - getDefaultConversation({ cTitle: WELCOME_CONVERSATION_TITLE, isFlyoutMode }); + getDefaultConversation({ cTitle: WELCOME_CONVERSATION_TITLE }); if ( prev && @@ -278,7 +262,6 @@ const AssistantComponent: React.FC = ({ getDefaultConversation, getLastConversationId, isAssistantEnabled, - isFlyoutMode, ]); // Welcome setup state @@ -295,10 +278,8 @@ const AssistantComponent: React.FC = ({ // Welcome conversation is a special 'setup' case when no connector exists, mostly extracted to `ConnectorSetup` component, // but currently a bit of state is littered throughout the assistant component. TODO: clean up/isolate this state const blockBotConversation = useMemo( - () => - currentConversation && - getBlockBotConversation(currentConversation, isAssistantEnabled, isFlyoutMode), - [currentConversation, isAssistantEnabled, isFlyoutMode] + () => currentConversation && getBlockBotConversation(currentConversation, isAssistantEnabled), + [currentConversation, isAssistantEnabled] ); // Settings modal state (so it isn't shared between assistant instances like Timeline) @@ -325,7 +306,6 @@ const AssistantComponent: React.FC = ({ setLastConversationId, ]); - const [promptTextPreview, setPromptTextPreview] = useState(''); const [autoPopulatedOnce, setAutoPopulatedOnce] = useState(false); const [userPrompt, setUserPrompt] = useState(null); @@ -398,16 +378,10 @@ const AssistantComponent: React.FC = ({ // when scrollHeight changes, parent is scrolled to bottom parent.scrollTop = parent.scrollHeight; - if (isFlyoutMode) { - ( - commentsContainerRef.current?.childNodes[0].childNodes[0] as HTMLElement - ).lastElementChild?.scrollIntoView(); - } + ( + commentsContainerRef.current?.childNodes[0].childNodes[0] as HTMLElement + ).lastElementChild?.scrollIntoView(); }); - - const getWrapper = (children: React.ReactNode, isCommentContainer: boolean) => - isCommentContainer ? {children} : <>{children}; - // End Scrolling const selectedSystemPrompt = useMemo( @@ -446,17 +420,6 @@ const AssistantComponent: React.FC = ({ [allSystemPrompts, refetchCurrentConversation, refetchResults] ); - const { comments: connectorComments, prompt: connectorPrompt } = useConnectorSetup({ - isFlyoutMode, - conversation: blockBotConversation, - onConversationUpdate: handleOnConversationSelected, - onSetupComplete: () => { - if (currentConversation) { - setCurrentConversation(clearPresentationData(currentConversation)); - } - }, - }); - const handleOnConversationDeleted = useCallback( async (cTitle: string) => { await deleteConversation(conversations[cTitle].id); @@ -538,14 +501,6 @@ const AssistantComponent: React.FC = ({ isFetchedAnonymizationFields, ]); - useEffect(() => {}, [ - areConnectorsFetched, - connectors, - conversationsLoaded, - currentConversation, - isLoading, - ]); - const createCodeBlockPortals = useCallback( () => messageCodeBlocks?.map((codeBlocks: CodeBlockDetails[], i: number) => { @@ -576,7 +531,6 @@ const AssistantComponent: React.FC = ({ } = useChatSend({ allSystemPrompts, currentConversation, - setPromptTextPreview, setUserPrompt, editingSystemPromptId, http, @@ -601,7 +555,7 @@ const AssistantComponent: React.FC = ({ [currentConversation, handleSendMessage, refetchResults] ); - const chatbotComments = useMemo( + const comments = useMemo( () => ( <> = ({ isFetchingResponse: isLoadingChatSend, setIsStreaming, currentUserAvatar, - isFlyoutMode, })} - {...(!isFlyoutMode - ? { - css: css` - margin-right: ${euiThemeVars.euiSizeL}; + // Avoid comments going off the flyout + css={css` + padding-bottom: ${euiThemeVars.euiSizeL}; - > li > div:nth-child(2) { - overflow: hidden; - } - `, - } - : { - // Avoid comments going off the flyout - css: css` - padding-bottom: ${euiThemeVars.euiSizeL}; - - > li > div:nth-child(2) { - overflow: hidden; - } - `, - })} + > li > div:nth-child(2) { + overflow: hidden; + } + `} /> {currentConversation?.messages.length !== 0 && selectedPromptContextsCount > 0 && ( )} - - {!isFlyoutMode && - (currentConversation?.messages.length === 0 || selectedPromptContextsCount > 0) && ( - - - - )} ), [ @@ -675,34 +596,10 @@ const AssistantComponent: React.FC = ({ isEnabledRAGAlerts, isLoadingChatSend, currentUserAvatar, - isFlyoutMode, selectedPromptContextsCount, - editingSystemPromptId, - isNewConversation, - isSettingsModalVisible, - promptContexts, - promptTextPreview, - handleOnSystemPromptSelectionChange, - selectedPromptContexts, - allSystemPrompts, ] ); - const comments = useMemo(() => { - if (isDisabled && !isFlyoutMode) { - return ( - - ); - } - - return chatbotComments; - }, [isDisabled, isFlyoutMode, chatbotComments, connectorComments]); - const trackPrompt = useCallback( (promptTitle: string) => { if (currentConversation?.title) { @@ -800,19 +697,14 @@ const AssistantComponent: React.FC = ({ textAlign="center" color={euiThemeVars.euiColorMediumShade} size="xs" - css={ - isFlyoutMode - ? css` - margin: 0 ${euiThemeVars.euiSizeL} ${euiThemeVars.euiSizeM} - ${euiThemeVars.euiSizeL}; - ` - : {} - } + css={css` + margin: 0 ${euiThemeVars.euiSizeL} ${euiThemeVars.euiSizeM} ${euiThemeVars.euiSizeL}; + `} > {i18n.DISCLAIMER} ), - [isFlyoutMode, isNewConversation] + [isNewConversation] ); const flyoutBodyContent = useMemo(() => { @@ -842,7 +734,10 @@ const AssistantComponent: React.FC = ({ - {connectorPrompt} + @@ -879,7 +774,6 @@ const AssistantComponent: React.FC = ({ onSystemPromptSelectionChange={handleOnSystemPromptSelectionChange} isSettingsModalVisible={isSettingsModalVisible} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode allSystemPrompts={allSystemPrompts} /> @@ -905,328 +799,212 @@ const AssistantComponent: React.FC = ({ ); }, [ allSystemPrompts, + blockBotConversation, comments, - connectorPrompt, currentConversation, editingSystemPromptId, + handleOnConversationSelected, handleOnSystemPromptSelectionChange, isSettingsModalVisible, isWelcomeSetup, ]); - if (isFlyoutMode) { - return ( - - {chatHistoryVisible && ( - - - - )} + return ( + + {chatHistoryVisible && ( - - + + )} + + + + - + + + {/* Create portals for each EuiCodeBlock to add the `Investigate in Timeline` action */} + {createCodeBlockPortals()} + + - - + min-height: 100px; + flex: 1; - {/* Create portals for each EuiCodeBlock to add the `Investigate in Timeline` action */} - {createCodeBlockPortals()} - - div { + display: flex; + flex-direction: column; + align-items: stretch; + + > .euiFlyoutBody__banner { + overflow-x: unset; + } - > div { + > .euiFlyoutBody__overflowContent { display: flex; - flex-direction: column; - align-items: stretch; - - > .euiFlyoutBody__banner { - overflow-x: unset; - } - - > .euiFlyoutBody__overflowContent { - display: flex; - flex: 1; - overflow: auto; - } + flex: 1; + overflow: auto; } - `} - banner={ - !isDisabled && - showMissingConnectorCallout && - areConnectorsFetched && ( - 0} - isSettingsModalVisible={isSettingsModalVisible} - setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={isFlyoutMode} - /> - ) } - > - {!isAssistantEnabled ? ( - 0} + isSettingsModalVisible={isSettingsModalVisible} + setIsSettingsModalVisible={setIsSettingsModalVisible} /> - ) : ( - - {flyoutBodyContent} - {disclaimer} - - )} - - + {!isAssistantEnabled ? ( + + } + http={http} + isAssistantEnabled={isAssistantEnabled} + isWelcomeSetup={isWelcomeSetup} + /> + ) : ( + + {flyoutBodyContent} + {disclaimer} + + )} + + + - - {!isDisabled && - Object.keys(promptContexts).length !== selectedPromptContextsCount && ( - - - <> - - {Object.keys(promptContexts).length > 0 && } - - - - )} - - - {Object.keys(selectedPromptContexts).length ? ( - - + {!isDisabled && + Object.keys(promptContexts).length !== selectedPromptContextsCount && ( + + + <> + + {Object.keys(promptContexts).length > 0 && } + - ) : null} + + )} + + {Object.keys(selectedPromptContexts).length ? ( - - - - - {!isDisabled && ( - - + - - )} - - - - - - - ); - } - - return getWrapper( - <> - - {showTitle && ( - - )} + + + - {/* Create portals for each EuiCodeBlock to add the `Investigate in Timeline` action */} - {createCodeBlockPortals()} - - {!isDisabled && !isLoadingAnonymizationFields && !isErrorAnonymizationFields && ( - <> - - {Object.keys(promptContexts).length > 0 && } - - )} - - - - - {' '} - {getWrapper( - <> - {comments} - - {!isDisabled && showMissingConnectorCallout && areConnectorsFetched && ( - <> - - - - 0} - isSettingsModalVisible={isSettingsModalVisible} - setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={isFlyoutMode} - /> - - - + {!isDisabled && ( + + + )} - , - !embeddedLayout - )} - - {disclaimer} - - - - - - {!isDisabled && ( - - )} - - , - embeddedLayout + + + + + + ); }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.test.tsx index 6d421b649a380..e2f55ee89202e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.test.tsx @@ -39,7 +39,6 @@ const defaultProps: Props = { selectedPromptContexts: {}, setIsSettingsModalVisible: jest.fn(), setSelectedPromptContexts: jest.fn(), - isFlyoutMode: false, allSystemPrompts: [], }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.tsx index 1528435764acd..adf9b7d4aa658 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/index.tsx @@ -31,7 +31,6 @@ export interface Props { setSelectedPromptContexts: React.Dispatch< React.SetStateAction> >; - isFlyoutMode: boolean; allSystemPrompts: PromptResponse[]; } @@ -50,7 +49,6 @@ const PromptEditorComponent: React.FC = ({ selectedPromptContexts, setIsSettingsModalVisible, setSelectedPromptContexts, - isFlyoutMode, allSystemPrompts, }) => { const commentBody = useMemo( @@ -64,17 +62,14 @@ const PromptEditorComponent: React.FC = ({ onSystemPromptSelectionChange={onSystemPromptSelectionChange} isSettingsModalVisible={isSettingsModalVisible} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={isFlyoutMode} /> )} @@ -90,7 +85,6 @@ const PromptEditorComponent: React.FC = ({ onSystemPromptSelectionChange, isSettingsModalVisible, setIsSettingsModalVisible, - isFlyoutMode, promptContexts, selectedPromptContexts, setSelectedPromptContexts, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.test.tsx index 899ee5ed7488c..873c41731bd20 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.test.tsx @@ -15,7 +15,6 @@ import type { SelectedPromptContext } from '../../prompt_context/types'; import { Props, SelectedPromptContexts } from '.'; const defaultProps: Props = { - isNewConversation: false, promptContexts: { [mockAlertPromptContext.id]: mockAlertPromptContext, [mockEventPromptContext.id]: mockEventPromptContext, @@ -23,7 +22,6 @@ const defaultProps: Props = { selectedPromptContexts: {}, setSelectedPromptContexts: jest.fn(), currentReplacements: {}, - isFlyoutMode: false, }; const mockSelectedAlertPromptContext: SelectedPromptContext = { @@ -53,61 +51,6 @@ describe('SelectedPromptContexts', () => { }); }); - it('it does NOT render a spacer when isNewConversation is false and selectedPromptContextIds.length is 1', async () => { - render( - - - - ); - - await waitFor(() => { - expect(screen.queryByTestId('spacer')).not.toBeInTheDocument(); - }); - }); - - it('it renders a spacer when isNewConversation is true and selectedPromptContextIds.length is 1', async () => { - render( - - - - ); - - await waitFor(() => { - expect(screen.getByTestId('spacer')).toBeInTheDocument(); - }); - }); - - it('it renders a spacer for each selected prompt context when isNewConversation is false and selectedPromptContextIds.length is 2', async () => { - render( - - - - ); - - await waitFor(() => { - expect(screen.getAllByTestId('spacer')).toHaveLength(2); - }); - }); - it('renders the selected prompt contexts', async () => { const selectedPromptContexts = { [mockAlertPromptContext.id]: mockSelectedAlertPromptContext, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.tsx index d3555b2e2ac86..3a0e6f3ce87d2 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/selected_prompt_contexts/index.tsx @@ -5,19 +5,10 @@ * 2.0. */ -import { - EuiAccordion, - EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiToolTip, -} from '@elastic/eui'; +import { EuiAccordion, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { isEmpty, omit } from 'lodash/fp'; import React, { useCallback } from 'react'; -// eslint-disable-next-line @kbn/eslint/module_migration -import styled from 'styled-components'; - +import styled from '@emotion/styled'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; import { Conversation } from '../../../assistant_context/types'; @@ -26,14 +17,12 @@ import type { PromptContext, SelectedPromptContext } from '../../prompt_context/ import * as i18n from './translations'; export interface Props { - isNewConversation: boolean; promptContexts: Record; selectedPromptContexts: Record; setSelectedPromptContexts: React.Dispatch< React.SetStateAction> >; currentReplacements: Conversation['replacements'] | undefined; - isFlyoutMode: boolean; } export const EditorContainer = styled.div<{ @@ -45,20 +34,11 @@ export const EditorContainer = styled.div<{ `; const SelectedPromptContextsComponent: React.FC = ({ - isNewConversation, promptContexts, selectedPromptContexts, setSelectedPromptContexts, currentReplacements, - isFlyoutMode, }) => { - const [accordionState, setAccordionState] = React.useState<'closed' | 'open'>('closed'); - - const onToggle = useCallback( - () => setAccordionState((prev) => (prev === 'open' ? 'closed' : 'open')), - [] - ); - const unselectPromptContext = useCallback( (unselectedId: string) => { setSelectedPromptContexts((prev) => omit(unselectedId, prev)); @@ -71,22 +51,13 @@ const SelectedPromptContextsComponent: React.FC = ({ } return ( - + {Object.keys(selectedPromptContexts) .sort() .map((id) => ( - {!isFlyoutMode && - (isNewConversation || Object.keys(selectedPromptContexts).length > 1) ? ( - - ) : null} = ({ } id={id} - {...(!isFlyoutMode && { onToggle })} paddingSize="s" - {...(isFlyoutMode - ? { - css: css` - background: ${euiThemeVars.euiPageBackgroundColor}; - border-radius: ${euiThemeVars.euiBorderRadius}; + css={css` + background: ${euiThemeVars.euiPageBackgroundColor}; + border-radius: ${euiThemeVars.euiBorderRadius}; - > div:first-child { - color: ${euiThemeVars.euiColorPrimary}; - padding: ${euiThemeVars.euiFormControlPadding}; - } - `, - borders: 'all', - arrowProps: { - color: 'primary', - }, - } - : {})} + > div:first-child { + color: ${euiThemeVars.euiColorPrimary}; + padding: ${euiThemeVars.euiFormControlPadding}; + } + `} + borders={'all'} + arrowProps={{ + color: 'primary', + }} > - {isFlyoutMode ? ( - - ) : ( - - - - )} + ))} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.test.tsx index 82b04c60a569c..f13441a3102f9 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.test.tsx @@ -16,21 +16,21 @@ import { getOptions, getOptionFromPrompt } from './helpers'; describe('helpers', () => { describe('getOptionFromPrompt', () => { it('returns an EuiSuperSelectOption with the correct value', () => { - const option = getOptionFromPrompt({ ...mockSystemPrompt, isFlyoutMode: true }); + const option = getOptionFromPrompt({ ...mockSystemPrompt }); expect(option.value).toBe(mockSystemPrompt.id); }); it('returns an EuiSuperSelectOption with the correct inputDisplay', () => { - const option = getOptionFromPrompt({ ...mockSystemPrompt, isFlyoutMode: false }); + const option = getOptionFromPrompt({ ...mockSystemPrompt }); render(<>{option.inputDisplay}); - expect(screen.getByTestId('systemPromptText')).toHaveTextContent(mockSystemPrompt.content); + expect(screen.getByTestId('systemPromptText')).toHaveTextContent(mockSystemPrompt.name); }); it('shows the expected name in the dropdownDisplay', () => { - const option = getOptionFromPrompt({ ...mockSystemPrompt, isFlyoutMode: true }); + const option = getOptionFromPrompt({ ...mockSystemPrompt }); render({option.dropdownDisplay}); @@ -38,7 +38,7 @@ describe('helpers', () => { }); it('shows the expected prompt content in the dropdownDisplay', () => { - const option = getOptionFromPrompt({ ...mockSystemPrompt, isFlyoutMode: true }); + const option = getOptionFromPrompt({ ...mockSystemPrompt }); render({option.dropdownDisplay}); @@ -51,7 +51,7 @@ describe('helpers', () => { const prompts = [mockSystemPrompt, mockSuperheroSystemPrompt]; const promptIds = prompts.map(({ id }) => id); - const options = getOptions({ prompts, isFlyoutMode: false }); + const options = getOptions({ prompts }); const optionValues = options.map(({ value }) => value); expect(optionValues).toEqual(promptIds); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.tsx index bd217bb54e9f6..92814927f980a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/helpers.tsx @@ -8,46 +8,23 @@ import { EuiText, EuiToolTip } from '@elastic/eui'; import type { EuiSuperSelectOption } from '@elastic/eui'; import React from 'react'; -// eslint-disable-next-line @kbn/eslint/module_migration -import styled from 'styled-components'; - -import { css } from '@emotion/react'; +import styled from '@emotion/styled'; import { isEmpty } from 'lodash/fp'; +import { euiThemeVars } from '@kbn/ui-theme'; import { PromptResponse } from '@kbn/elastic-assistant-common'; import { EMPTY_PROMPT } from './translations'; const Strong = styled.strong` - margin-right: ${({ theme }) => theme.eui.euiSizeS}; + margin-right: ${euiThemeVars.euiSizeS}; `; export const getOptionFromPrompt = ({ content, id, name, - showTitles = false, - isFlyoutMode, -}: PromptResponse & { - showTitles?: boolean; - isFlyoutMode: boolean; -}): EuiSuperSelectOption => ({ +}: PromptResponse): EuiSuperSelectOption => ({ value: id, - inputDisplay: isFlyoutMode ? ( - name - ) : ( - - {showTitles ? name : content} - - ), + inputDisplay: {name}, dropdownDisplay: ( <> {name} @@ -64,12 +41,6 @@ export const getOptionFromPrompt = ({ interface GetOptionsProps { prompts: PromptResponse[] | undefined; - showTitles?: boolean; - isFlyoutMode: boolean; } -export const getOptions = ({ - prompts, - showTitles = false, - isFlyoutMode, -}: GetOptionsProps): Array> => - prompts?.map((p) => getOptionFromPrompt({ ...p, showTitles, isFlyoutMode })) ?? []; +export const getOptions = ({ prompts }: GetOptionsProps): Array> => + prompts?.map(getOptionFromPrompt) ?? []; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx index 34d40852ba505..3b82b1fd0fbe5 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.test.tsx @@ -90,7 +90,6 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> ); @@ -100,10 +99,6 @@ describe('SystemPrompt', () => { expect(screen.getByTestId('selectSystemPrompt')).toBeInTheDocument(); }); - it('does NOT render the system prompt text', () => { - expect(screen.queryByTestId('systemPromptText')).not.toBeInTheDocument(); - }); - it('does NOT render the edit button', () => { expect(screen.queryByTestId('edit')).not.toBeInTheDocument(); }); @@ -122,26 +117,21 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> ); }); - it('does NOT render the system prompt select', () => { - expect(screen.queryByTestId('selectSystemPrompt')).not.toBeInTheDocument(); + it('does render the system prompt select', () => { + expect(screen.queryByTestId('selectSystemPrompt')).toBeInTheDocument(); }); it('renders the system prompt text', () => { - expect(screen.getByTestId('systemPromptText')).toHaveTextContent(mockSystemPrompt.content); - }); - - it('renders the edit button', () => { - expect(screen.getByTestId('edit')).toBeInTheDocument(); + expect(screen.getByTestId('systemPromptText')).toHaveTextContent(mockSystemPrompt.name); }); it('renders the clear button', () => { - expect(screen.getByTestId('clear')).toBeInTheDocument(); + expect(screen.getByTestId('clearSystemPrompt')).toBeInTheDocument(); }); }); @@ -158,7 +148,6 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> @@ -206,7 +195,6 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> @@ -268,7 +256,6 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> @@ -337,7 +324,6 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> @@ -421,7 +407,6 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> @@ -483,26 +468,6 @@ describe('SystemPrompt', () => { }); }); - it('shows the system prompt select when the edit button is clicked', () => { - render( - - - - ); - - userEvent.click(screen.getByTestId('edit')); - - expect(screen.getByTestId('selectSystemPrompt')).toBeInTheDocument(); - }); - it('shows the system prompt select when system prompt text is clicked', () => { render( @@ -512,7 +477,6 @@ describe('SystemPrompt', () => { isSettingsModalVisible={isSettingsModalVisible} onSystemPromptSelectionChange={onSystemPromptSelectionChange} setIsSettingsModalVisible={setIsSettingsModalVisible} - isFlyoutMode={false} allSystemPrompts={mockSystemPrompts} /> diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.tsx index f2808c3e204f1..01fe334eb1f7d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/index.tsx @@ -5,14 +5,9 @@ * 2.0. */ -import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; - -import { css } from '@emotion/react'; -import { isEmpty } from 'lodash/fp'; import { PromptResponse } from '@kbn/elastic-assistant-common'; import { Conversation } from '../../../..'; -import * as i18n from './translations'; import { SelectSystemPrompt } from './select_system_prompt'; interface Props { @@ -21,7 +16,6 @@ interface Props { isSettingsModalVisible: boolean; onSystemPromptSelectionChange: (systemPromptId: string | undefined) => void; setIsSettingsModalVisible: React.Dispatch>; - isFlyoutMode: boolean; allSystemPrompts: PromptResponse[]; } @@ -31,7 +25,6 @@ const SystemPromptComponent: React.FC = ({ isSettingsModalVisible, onSystemPromptSelectionChange, setIsSettingsModalVisible, - isFlyoutMode, allSystemPrompts, }) => { const selectedPrompt = useMemo(() => { @@ -42,99 +35,24 @@ const SystemPromptComponent: React.FC = ({ } }, [allSystemPrompts, conversation?.apiConfig?.defaultSystemPromptId, editingSystemPromptId]); - const [isEditing, setIsEditing] = React.useState(false); - const handleClearSystemPrompt = useCallback(() => { if (conversation) { onSystemPromptSelectionChange(undefined); } }, [conversation, onSystemPromptSelectionChange]); - const handleEditSystemPrompt = useCallback(() => setIsEditing(true), []); - - if (isFlyoutMode) { - return ( - - ); - } - return ( -
- {selectedPrompt == null || isEditing ? ( - - ) : ( - - - - {isEmpty(selectedPrompt?.content) ? i18n.EMPTY_PROMPT : selectedPrompt?.content} - - - - - - - - - - - - - - - - - - - )} -
+ ); }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.test.tsx index 3796e5b4a81eb..7c8f575cd49d7 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.test.tsx @@ -48,9 +48,9 @@ const props: Props = { ], conversation: undefined, isSettingsModalVisible: false, + isClearable: true, selectedPrompt: { id: 'default-system-prompt', content: '', name: '', promptType: 'system' }, setIsSettingsModalVisible: jest.fn(), - isFlyoutMode: false, }; const mockUseAssistantContext = { @@ -91,93 +91,27 @@ jest.mock('../../../../assistant_context', () => { describe('SelectSystemPrompt', () => { beforeEach(() => jest.clearAllMocks()); - it('renders the prompt super select when isEditing is true', () => { - const { getByTestId } = render(); + it('renders the prompt super select', () => { + const { getByTestId } = render(); expect(getByTestId(TEST_IDS.PROMPT_SUPERSELECT)).toBeInTheDocument(); }); - it('does NOT render the prompt super select when isEditing is false', () => { - const { queryByTestId } = render(); - - expect(queryByTestId(TEST_IDS.PROMPT_SUPERSELECT)).not.toBeInTheDocument(); - }); - - it('does NOT render the clear system prompt button when isEditing is true', () => { - const { queryByTestId } = render(); - - expect(queryByTestId('clearSystemPrompt')).not.toBeInTheDocument(); - }); - - it('renders the clear system prompt button when isEditing is true AND isClearable is true', () => { - const { getByTestId } = render( - - ); + it('renders the clear system prompt button', () => { + const { getByTestId } = render(); expect(getByTestId('clearSystemPrompt')).toBeInTheDocument(); }); - it('does NOT render the clear system prompt button when isEditing is false', () => { - const { queryByTestId } = render(); - - expect(queryByTestId('clearSystemPrompt')).not.toBeInTheDocument(); - }); - - it('renders the add system prompt button when isEditing is false', () => { - const { getByTestId } = render(); - - expect(getByTestId('addSystemPrompt')).toBeInTheDocument(); - }); - - it('does NOT render the add system prompt button when isEditing is true', () => { - const { queryByTestId } = render(); - - expect(queryByTestId('addSystemPrompt')).not.toBeInTheDocument(); - }); - it('clears the selected system prompt when the clear button is clicked', () => { const clearSelectedSystemPrompt = jest.fn(); const { getByTestId } = render( - + ); userEvent.click(getByTestId('clearSystemPrompt')); expect(clearSelectedSystemPrompt).toHaveBeenCalledTimes(1); }); - - it('hides the select when the clear button is clicked', () => { - const setIsEditing = jest.fn(); - - const { getByTestId } = render( - - ); - - userEvent.click(getByTestId('clearSystemPrompt')); - - expect(setIsEditing).toHaveBeenCalledWith(false); - }); - - it('shows the select when the add button is clicked', () => { - const setIsEditing = jest.fn(); - - const { getByTestId } = render( - - ); - - userEvent.click(getByTestId('addSystemPrompt')); - - expect(setIsEditing).toHaveBeenCalledWith(true); - }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.tsx index 0296fa3e636ca..0f10cf6d3063f 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/select_system_prompt/index.tsx @@ -38,15 +38,11 @@ export interface Props { selectedPrompt: PromptResponse | undefined; clearSelectedSystemPrompt?: () => void; isClearable?: boolean; - isEditing?: boolean; isDisabled?: boolean; isOpen?: boolean; isSettingsModalVisible: boolean; - setIsEditing?: React.Dispatch>; setIsSettingsModalVisible: React.Dispatch>; - showTitles?: boolean; onSystemPromptSelectionChange?: (promptId: string | undefined) => void; - isFlyoutMode: boolean; } const ADD_NEW_SYSTEM_PROMPT = 'ADD_NEW_SYSTEM_PROMPT'; @@ -58,15 +54,11 @@ const SelectSystemPromptComponent: React.FC = ({ selectedPrompt, clearSelectedSystemPrompt, isClearable = false, - isEditing = false, isDisabled = false, isOpen = false, isSettingsModalVisible, onSystemPromptSelectionChange, - setIsEditing, setIsSettingsModalVisible, - showTitles = false, - isFlyoutMode = false, }) => { const { setSelectedSettingsTab } = useAssistantContext(); const { setApiConfig } = useConversation(); @@ -117,10 +109,7 @@ const SelectSystemPromptComponent: React.FC = ({ }, []); // SuperSelect State/Actions - const options = useMemo( - () => getOptions({ prompts: allSystemPrompts, showTitles, isFlyoutMode }), - [allSystemPrompts, showTitles, isFlyoutMode] - ); + const options = useMemo(() => getOptions({ prompts: allSystemPrompts }), [allSystemPrompts]); const onChange = useCallback( (selectedSystemPromptId) => { @@ -134,11 +123,9 @@ const SelectSystemPromptComponent: React.FC = ({ onSystemPromptSelectionChange(selectedSystemPromptId); } setSelectedSystemPrompt(selectedSystemPromptId); - setIsEditing?.(false); }, [ onSystemPromptSelectionChange, - setIsEditing, setIsSettingsModalVisible, setSelectedSettingsTab, setSelectedSystemPrompt, @@ -147,14 +134,8 @@ const SelectSystemPromptComponent: React.FC = ({ const clearSystemPrompt = useCallback(() => { setSelectedSystemPrompt(undefined); - setIsEditing?.(false); clearSelectedSystemPrompt?.(); - }, [clearSelectedSystemPrompt, setIsEditing, setSelectedSystemPrompt]); - - const onShowSelectSystemPrompt = useCallback(() => { - setIsEditing?.(true); - setIsOpenLocal(true); - }, [setIsEditing]); + }, [clearSelectedSystemPrompt, setSelectedSystemPrompt]); return ( = ({ max-width: 100%; `} > - {isEditing && ( - + - - - )} + /> + - {isEditing && isClearable && selectedPrompt && ( + {isClearable && selectedPrompt && ( svg { - width: 8px; - height: 8px; - stroke-width: 2px; - fill: #fff; - stroke: #fff; - } - ` - : undefined - } - /> - - )} - {!isEditing && ( - - svg { + width: 8px; + height: 8px; + stroke-width: 2px; + fill: #fff; + stroke: #fff; + } + `} /> )} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.tsx index 2c4826940a7ca..ae5fce935cfe3 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.tsx @@ -147,9 +147,8 @@ export const SystemPromptSelector: React.FC = React.memo( const renderOption: ( option: SystemPromptSelectorOption, - searchValue: string, - OPTION_CONTENT_CLASSNAME: string - ) => React.ReactNode = (option, searchValue, contentClassName) => { + searchValue: string + ) => React.ReactNode = (option, searchValue) => { const { label, value } = option; return ( isDisabled?: boolean; onPromptSubmit: (value: string) => void; value: string; - isFlyoutMode: boolean; } export const PromptTextArea = forwardRef( - ({ isDisabled = false, value, onPromptSubmit, handlePromptChange, isFlyoutMode }, ref) => { + ({ isDisabled = false, value, onPromptSubmit, handlePromptChange }, ref) => { const onChangeCallback = useCallback( (event: React.ChangeEvent) => { handlePromptChange(event.target.value); @@ -46,8 +45,8 @@ export const PromptTextArea = forwardRef( ( value={value} onChange={onChangeCallback} onKeyDown={onKeyDown} - rows={isFlyoutMode ? 1 : 6} + rows={1} /> ); } diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_selector/quick_prompt_selector.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_selector/quick_prompt_selector.tsx index d29887e8c4f6a..759c6e49e446e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_selector/quick_prompt_selector.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_selector/quick_prompt_selector.tsx @@ -140,9 +140,8 @@ export const QuickPromptSelector: React.FC = React.memo( const renderOption: ( option: QuickPromptSelectorOption, - searchValue: string, - OPTION_CONTENT_CLASSNAME: string - ) => React.ReactNode = (option, searchValue, contentClassName) => { + searchValue: string + ) => React.ReactNode = (option, searchValue) => { const { color, label, value } = option; return ( ({ + ...jest.requireActual('react-use'), + useMeasure: () => [ + () => {}, + { + width: 500, + }, + ], +})); + jest.mock('../../assistant_context', () => ({ ...jest.requireActual('../../assistant_context'), useAssistantContext: () => mockUseAssistantContext, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompts.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompts.tsx index c578a58be728d..e910d238ccc5d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompts.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompts.tsx @@ -27,12 +27,10 @@ import { QUICK_PROMPTS_TAB } from '../settings/const'; export const KNOWLEDGE_BASE_CATEGORY = 'knowledge-base'; -const COUNT_BEFORE_OVERFLOW = 5; interface QuickPromptsProps { setInput: (input: string) => void; setIsSettingsModalVisible: React.Dispatch>; trackPrompt: (prompt: string) => void; - isFlyoutMode: boolean; allPrompts: PromptResponse[]; } @@ -42,7 +40,7 @@ interface QuickPromptsProps { * and localstorage for storing new and edited prompts. */ export const QuickPrompts: React.FC = React.memo( - ({ setInput, setIsSettingsModalVisible, trackPrompt, isFlyoutMode, allPrompts }) => { + ({ setInput, setIsSettingsModalVisible, trackPrompt, allPrompts }) => { const [quickPromptsContainerRef, { width }] = useMeasure(); const { knowledgeBase, promptContexts, setSelectedSettingsTab } = useAssistantContext(); @@ -103,25 +101,15 @@ export const QuickPrompts: React.FC = React.memo( }, [setIsSettingsModalVisible, setSelectedSettingsTab]); const quickPrompts = useMemo(() => { - const visibleCount = isFlyoutMode ? Math.floor(width / 120) : COUNT_BEFORE_OVERFLOW; + const visibleCount = Math.floor(width / 120); const visibleItems = contextFilteredQuickPrompts.slice(0, visibleCount); const overflowItems = contextFilteredQuickPrompts.slice(visibleCount); return { visible: visibleItems, overflow: overflowItems }; - }, [contextFilteredQuickPrompts, isFlyoutMode, width]); + }, [contextFilteredQuickPrompts, width]); return ( - + = React.memo( - ) : ( - - ) + } isOpen={isOverflowPopoverOpen} closePopover={closeOverflowPopover} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx index 8f4a8680f9c57..9fb8db972e482 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.test.tsx @@ -55,7 +55,6 @@ const testProps = { selectedConversationId: welcomeConvo.title, onClose, onSave, - isFlyoutMode: false, onConversationSelected, conversations: {}, anonymizationFields: { total: 0, page: 1, perPage: 1000, data: [] }, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx index d5bbefe304208..4b46d2b75d0a9 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx @@ -59,7 +59,6 @@ interface Props { onClose: ( event?: React.KeyboardEvent | React.MouseEvent ) => void; - isFlyoutMode: boolean; onSave: (success: boolean) => Promise; selectedConversationId?: string; onConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => void; @@ -80,7 +79,6 @@ export const AssistantSettings: React.FC = React.memo( onConversationSelected, conversations, conversationsLoaded, - isFlyoutMode, }) => { const { actionTypeRegistry, @@ -338,7 +336,6 @@ export const AssistantSettings: React.FC = React.memo( setAssistantStreamingEnabled={setUpdatedAssistantStreamingEnabled} onSelectedConversationChange={onHandleSelectedConversationChange} http={http} - isFlyoutMode={isFlyoutMode} /> ))} {selectedSettingsTab === QUICK_PROMPTS_TAB && ( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.test.tsx index 3aab8d1169bfc..0ef76adad9940 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.test.tsx @@ -22,7 +22,6 @@ const testProps = { isSettingsModalVisible: false, selectedConversation: welcomeConvo, setIsSettingsModalVisible, - isFlyoutMode: false, onConversationSelected, conversations: {}, conversationsLoaded: true, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx index 30f141f219476..0df20b0cd4db2 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_button.tsx @@ -23,7 +23,6 @@ interface Props { setIsSettingsModalVisible: React.Dispatch>; onConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => void; isDisabled?: boolean; - isFlyoutMode: boolean; conversations: Record; conversationsLoaded: boolean; refetchConversationsState: () => Promise; @@ -42,7 +41,6 @@ export const AssistantSettingsButton: React.FC = React.memo( isSettingsModalVisible, setIsSettingsModalVisible, selectedConversationId, - isFlyoutMode, onConversationSelected, conversations, conversationsLoaded, @@ -92,7 +90,7 @@ export const AssistantSettingsButton: React.FC = React.memo( isDisabled={isDisabled} iconType="gear" size="xs" - {...(isFlyoutMode ? { color: 'text' } : {})} + color="text" /> @@ -103,7 +101,6 @@ export const AssistantSettingsButton: React.FC = React.memo( onConversationSelected={onConversationSelected} onClose={handleCloseModal} onSave={handleSave} - isFlyoutMode={isFlyoutMode} conversations={conversations} conversationsLoaded={conversationsLoaded} /> diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.test.tsx index 15fb05ca1c807..7d70ee5ede730 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.test.tsx @@ -62,7 +62,6 @@ const testProps = { selectedConversation: welcomeConvo, onClose, onSave, - isFlyoutMode: false, onConversationSelected, conversations: {}, anonymizationFields: { total: 0, page: 1, perPage: 1000, data: [] }, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.tsx index 3f9be4972fe7e..be6370d36e841 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings_management.tsx @@ -49,7 +49,6 @@ interface Props { conversations: Record; conversationsLoaded: boolean; selectedConversation: Conversation; - isFlyoutMode: boolean; refetchConversations: () => void; } @@ -61,7 +60,6 @@ export const AssistantSettingsManagement: React.FC = React.memo( ({ conversations, conversationsLoaded, - isFlyoutMode, refetchConversations, selectedConversation: defaultSelectedConversation, }) => { @@ -304,7 +302,6 @@ export const AssistantSettingsManagement: React.FC = React.memo( conversationsSettingsBulkActions={conversationsSettingsBulkActions} defaultConnector={defaultConnector} handleSave={handleSave} - isFlyoutMode={isFlyoutMode} onCancelClick={onCancelClick} onSelectedConversationChange={onHandleSelectedConversationChange} selectedConversation={selectedConversation} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx index fe4d75be04004..71bbab7636a4a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/evaluation_settings.tsx @@ -44,14 +44,10 @@ const DEFAULT_EVAL_TYPES_OPTIONS = [ ]; const DEFAULT_OUTPUT_INDEX = '.kibana-elastic-ai-assistant-evaluation-results'; -interface Props { - onEvaluationSettingsChange?: () => void; -} - /** * Evaluation Settings -- development-only feature for evaluating models */ -export const EvaluationSettings: React.FC = React.memo(({ onEvaluationSettingsChange }) => { +export const EvaluationSettings: React.FC = React.memo(() => { const { actionTypeRegistry, basePath, http, setTraceOptions, traceOptions } = useAssistantContext(); const { data: connectors } = useLoadConnectors({ http }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx index 3396223d192ca..9ccc2cbce815d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx @@ -130,7 +130,7 @@ export const useAssistantOverlay = ( // proxy show / hide calls to assistant context, using our internal prompt context id: // silent:boolean doesn't show the toast notification if the conversation is not found const showAssistantOverlay = useCallback( - async (showOverlay: boolean, silent?: boolean) => { + async (showOverlay: boolean) => { let conversation; if (!isLoading) { conversation = conversationTitle diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx index a276aea3ff4ab..4643af5509aeb 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/index.tsx @@ -33,7 +33,6 @@ interface CreateConversationProps { messages?: ClientMessage[]; conversationIds?: string[]; apiConfig?: Conversation['apiConfig']; - isFlyoutMode: boolean; } interface SetApiConfigProps { @@ -126,13 +125,10 @@ export const useConversation = (): UseConversation => { * Create a new conversation with the given conversationId, and optionally add messages */ const getDefaultConversation = useCallback( - ({ cTitle, messages, isFlyoutMode }: CreateConversationProps): Conversation => { + ({ cTitle, messages }: CreateConversationProps): Conversation => { const newConversation: Conversation = cTitle === i18n.WELCOME_CONVERSATION_TITLE - ? { - ...WELCOME_CONVERSATION, - messages: !isFlyoutMode ? WELCOME_CONVERSATION.messages : [], - } + ? WELCOME_CONVERSATION : { ...DEFAULT_CONVERSATION_STATE, id: '', diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx index 7cdf709192f70..85192f646963c 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_conversation/sample_conversations.tsx @@ -13,35 +13,7 @@ export const WELCOME_CONVERSATION: Conversation = { id: '', title: WELCOME_CONVERSATION_TITLE, category: 'assistant', - messages: [ - { - role: 'assistant', - content: i18n.WELCOME_GENERAL, - timestamp: '', - presentation: { - delay: 2 * 1000, - stream: true, - }, - }, - { - role: 'assistant', - content: i18n.WELCOME_GENERAL_2, - timestamp: '', - presentation: { - delay: 1000, - stream: true, - }, - }, - { - role: 'assistant', - content: i18n.WELCOME_GENERAL_3, - timestamp: '', - presentation: { - delay: 1000, - stream: true, - }, - }, - ], + messages: [], replacements: {}, excludeFromLastConversationStorage: true, }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx index 78336f8a8b03d..65fca75623306 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx @@ -73,7 +73,6 @@ export interface AssistantProviderProps { showAnonymizedValues: boolean; setIsStreaming: (isStreaming: boolean) => void; currentUserAvatar?: UserAvatar; - isFlyoutMode: boolean; }) => EuiCommentProps[]; http: HttpSetup; baseConversations: Record; @@ -114,7 +113,6 @@ export interface UseAssistantContext { showAnonymizedValues: boolean; currentUserAvatar?: UserAvatar; setIsStreaming: (isStreaming: boolean) => void; - isFlyoutMode: boolean; }) => EuiCommentProps[]; http: HttpSetup; knowledgeBase: KnowledgeBaseConfig; @@ -234,9 +232,7 @@ export const AssistantProvider: React.FC = ({ /** * Global Assistant Overlay actions */ - const [showAssistantOverlay, setShowAssistantOverlay] = useState( - (showAssistant) => {} - ); + const [showAssistantOverlay, setShowAssistantOverlay] = useState(() => {}); /** * Settings State diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.test.tsx index a131e63ae49c3..5465ca19e99de 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.test.tsx @@ -30,7 +30,6 @@ describe('connectorMissingCallout', () => { isConnectorConfigured={false} isSettingsModalVisible={false} setIsSettingsModalVisible={jest.fn()} - isFlyoutMode={false} /> ); @@ -45,7 +44,6 @@ describe('connectorMissingCallout', () => { isConnectorConfigured={true} isSettingsModalVisible={false} setIsSettingsModalVisible={jest.fn()} - isFlyoutMode={false} /> ); @@ -70,7 +68,6 @@ describe('connectorMissingCallout', () => { isConnectorConfigured={true} isSettingsModalVisible={false} setIsSettingsModalVisible={jest.fn()} - isFlyoutMode={false} /> ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.tsx index 8853ca0a67d33..26ce2f736ed9a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_missing_callout/index.tsx @@ -20,7 +20,6 @@ interface Props { isConnectorConfigured: boolean; isSettingsModalVisible: boolean; setIsSettingsModalVisible: React.Dispatch>; - isFlyoutMode: boolean; } /** @@ -31,7 +30,7 @@ interface Props { * TODO: Add setting for 'default connector' so we can auto-resolve and not even show this */ export const ConnectorMissingCallout: React.FC = React.memo( - ({ isConnectorConfigured, isSettingsModalVisible, setIsSettingsModalVisible, isFlyoutMode }) => { + ({ isConnectorConfigured, isSettingsModalVisible, setIsSettingsModalVisible }) => { const { assistantAvailability, setSelectedSettingsTab } = useAssistantContext(); const onConversationSettingsClicked = useCallback(() => { @@ -55,13 +54,10 @@ export const ConnectorMissingCallout: React.FC = React.memo( iconType="controlsVertical" size="m" title={i18n.MISSING_CONNECTOR_CALLOUT_TITLE} - css={ - isFlyoutMode && - css` - padding-left: ${euiLightVars.euiPanelPaddingModifiers.paddingMedium} !important; - padding-right: ${euiLightVars.euiPanelPaddingModifiers.paddingMedium} !important; - ` - } + css={css` + padding-left: ${euiLightVars.euiPanelPaddingModifiers.paddingMedium} !important; + padding-right: ${euiLightVars.euiPanelPaddingModifiers.paddingMedium} !important; + `} >

{ beforeEach(() => { jest.clearAllMocks(); }); - it('renders empty selection if no selected connector is provided', () => { + it('renders add new connector button if no selected connector is provided', () => { const { getByTestId } = render( ); - expect(getByTestId('connector-selector')).toBeInTheDocument(); - expect(getByTestId('connector-selector')).toHaveTextContent(''); + fireEvent.click(getByTestId('connector-selector')); + expect(getByTestId('addNewConnectorButton')).toBeInTheDocument(); }); it('renders with provided selected connector', () => { const { getByTestId } = render( diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx index 410ee650c43ef..ad0fffc44e6b5 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx @@ -30,7 +30,6 @@ interface Props { selectedConnectorId?: string; displayFancy?: (displayText: string) => React.ReactNode; setIsOpen?: (isOpen: boolean) => void; - isFlyoutMode: boolean; stats?: AttackDiscoveryStats | null; } @@ -47,7 +46,6 @@ export const ConnectorSelector: React.FC = React.memo( selectedConnectorId, onConnectorSelectionChange, setIsOpen, - isFlyoutMode, stats = null, }) => { const { actionTypeRegistry, http, assistantAvailability } = useAssistantContext(); @@ -177,7 +175,7 @@ export const ConnectorSelector: React.FC = React.memo( return ( <> - {isFlyoutMode && !connectorExists && !connectorOptions.length ? ( + {!connectorExists && !connectorOptions.length ? ( ({ jest.mock('../use_load_connectors', () => ({ useLoadConnectors: jest.fn(() => { return { - data: [], + data: mockConnectors, error: null, isSuccess: true, }; @@ -68,67 +67,61 @@ describe('ConnectorSelectorInline', () => { jest.clearAllMocks(); }); it('renders empty view if no selected conversation is provided', () => { - const { getByText } = render( + const { getByTestId } = render( ); - expect(getByText(i18n.INLINE_CONNECTOR_PLACEHOLDER)).toBeInTheDocument(); + fireEvent.click(getByTestId('connector-selector')); + expect(getByTestId('addNewConnectorButton')).toBeInTheDocument(); }); it('renders empty view if selectedConnectorId is NOT in list of connectors', () => { - const { getByText } = render( + const { getByTestId } = render( ); - expect(getByText(i18n.INLINE_CONNECTOR_PLACEHOLDER)).toBeInTheDocument(); + fireEvent.click(getByTestId('connector-selector')); + expect(getByTestId('addNewConnectorButton')).toBeInTheDocument(); }); - it('Clicking add connector button opens the connector selector', () => { - const { getByTestId, queryByTestId } = render( + it('renders the connector selector', () => { + const { getByTestId } = render( ); - expect(queryByTestId('connector-selector')).not.toBeInTheDocument(); - fireEvent.click(getByTestId('connectorSelectorPlaceholderButton')); expect(getByTestId('connector-selector')).toBeInTheDocument(); }); it('On connector change, update conversation API config', () => { const connectorTwo = mockConnectors[1]; - const { getByTestId, queryByTestId } = render( + const { getByTestId } = render( ); - fireEvent.click(getByTestId('connectorSelectorPlaceholderButton')); fireEvent.click(getByTestId('connector-selector')); fireEvent.click(getByTestId(connectorTwo.id)); - expect(queryByTestId('connector-selector')).not.toBeInTheDocument(); expect(setApiConfig).toHaveBeenCalledWith({ apiConfig: { actionTypeId: '.gen-ai', @@ -151,16 +144,13 @@ describe('ConnectorSelectorInline', () => { ); - fireEvent.click(getByTestId('connectorSelectorPlaceholderButton')); fireEvent.click(getByTestId('connector-selector')); - fireEvent.click(getByTestId('addNewConnectorButton')); expect(getByTestId('connector-selector')).toBeInTheDocument(); expect(setApiConfig).not.toHaveBeenCalled(); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx index ebf762530af11..19e5db98a74fa 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import React, { useCallback, useState } from 'react'; import { css } from '@emotion/css'; @@ -13,8 +13,6 @@ import { euiThemeVars } from '@kbn/ui-theme'; import type { AttackDiscoveryStats } from '@kbn/elastic-assistant-common'; import { AIConnector, ConnectorSelector } from '../connector_selector'; import { Conversation } from '../../..'; -import { useLoadConnectors } from '../use_load_connectors'; -import * as i18n from '../translations'; import { useAssistantContext } from '../../assistant_context'; import { useConversation } from '../../assistant/use_conversation'; import { getGenAiConfig } from '../helpers'; @@ -25,7 +23,6 @@ interface Props { isDisabled?: boolean; selectedConnectorId?: string; selectedConversation?: Conversation; - isFlyoutMode: boolean; onConnectorIdSelected?: (connectorId: string) => void; onConnectorSelected?: (conversation: Conversation) => void; stats?: AttackDiscoveryStats | null; @@ -53,14 +50,6 @@ const inputDisplayClassName = css` text-overflow: ellipsis; `; -const placeholderButtonClassName = css` - overflow: hidden; - text-overflow: ellipsis; - max-width: 400px; - font-weight: normal; - padding: 0 14px 0 0; -`; - /** * A compact wrapper of the ConnectorSelector component used in the Settings modal. */ @@ -69,29 +58,16 @@ export const ConnectorSelectorInline: React.FC = React.memo( isDisabled = false, selectedConnectorId, selectedConversation, - isFlyoutMode, - onConnectorIdSelected, onConnectorSelected, stats = null, }) => { const [isOpen, setIsOpen] = useState(false); - const { assistantAvailability, http } = useAssistantContext(); + const { assistantAvailability } = useAssistantContext(); const { setApiConfig } = useConversation(); - const { data: aiConnectors } = useLoadConnectors({ - http, - }); - - const selectedConnectorName = - (aiConnectors ?? []).find((c) => c.id === selectedConnectorId)?.name ?? - i18n.INLINE_CONNECTOR_PLACEHOLDER; const localIsDisabled = isDisabled || !assistantAvailability.hasConnectorsReadPrivilege; - const onConnectorClick = useCallback(() => { - setIsOpen(!isOpen); - }, [isOpen]); - const onChange = useCallback( async (connector: AIConnector) => { const connectorId = connector.id; @@ -129,40 +105,6 @@ export const ConnectorSelectorInline: React.FC = React.memo( [selectedConversation, setApiConfig, onConnectorIdSelected, onConnectorSelected] ); - if (isFlyoutMode) { - return ( - - - ( - - {displayText} - - )} - isOpen={isOpen} - isDisabled={localIsDisabled} - selectedConnectorId={selectedConnectorId} - setIsOpen={setIsOpen} - onConnectorSelectionChange={onChange} - isFlyoutMode={isFlyoutMode} - stats={stats} - /> - - - ); - } - return ( = React.memo( responsive={false} > - {isOpen ? ( - ( - - {displayText} - - )} - isOpen - isDisabled={localIsDisabled} - selectedConnectorId={selectedConnectorId} - setIsOpen={setIsOpen} - onConnectorSelectionChange={onChange} - isFlyoutMode={isFlyoutMode} - stats={stats} - /> - ) : ( - - ( + - {selectedConnectorName} - - - )} + {displayText} + + )} + isOpen={isOpen} + isDisabled={localIsDisabled} + selectedConnectorId={selectedConnectorId} + setIsOpen={setIsOpen} + onConnectorSelectionChange={onChange} + stats={stats} + /> ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/helpers.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/helpers.tsx deleted file mode 100644 index cb11ca51047f0..0000000000000 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/helpers.tsx +++ /dev/null @@ -1,33 +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 { Conversation } from '../../assistant_context/types'; - -/** - * Removes all presentation data from the conversation - * @param conversation - */ -export const clearPresentationData = (conversation: Conversation): Conversation => { - const { messages, ...restConversation } = conversation; - return { - ...restConversation, - messages: messages.map((message) => { - const { presentation, ...restMessages } = message; - return { - ...restMessages, - presentation: undefined, - }; - }), - }; -}; - -/** - * Returns true if the conversation has no presentation data - * @param conversation - */ -export const conversationHasNoPresentationData = (conversation: Conversation): boolean => - !conversation.messages.some((message) => message.presentation !== undefined); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.test.tsx index cf46b5886a389..b6eaa4578d4a0 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.test.tsx @@ -6,19 +6,15 @@ */ import React from 'react'; -import { useConnectorSetup } from '.'; -import { act, renderHook } from '@testing-library/react-hooks'; import { fireEvent, render } from '@testing-library/react'; import { welcomeConvo } from '../../mock/conversation'; import { TestProviders } from '../../mock/test_providers/test_providers'; -import { EuiCommentList } from '@elastic/eui'; +import { ConnectorSetup } from '.'; -const onSetupComplete = jest.fn(); const onConversationUpdate = jest.fn(); const defaultProps = { conversation: welcomeConvo, - onSetupComplete, onConversationUpdate, }; const newConnector = { actionTypeId: '.gen-ai', name: 'cool name' }; @@ -50,121 +46,40 @@ jest.mock('../../assistant/use_conversation', () => ({ })); jest.spyOn(global, 'clearTimeout'); -describe('useConnectorSetup', () => { +describe('ConnectorSetup', () => { beforeEach(() => { jest.clearAllMocks(); }); - it('should render comments and prompts', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useConnectorSetup(defaultProps), { - wrapper: ({ children }) => {children}, - }); - await waitForNextUpdate(); - expect( - result.current.comments.map((c) => ({ username: c.username, timestamp: c.timestamp })) - ).toEqual([ - { - username: 'You', - timestamp: `at: ${new Date('2024-03-18T18:59:18.174Z').toLocaleString()}`, - }, - { - username: 'Assistant', - timestamp: `at: ${new Date('2024-03-19T18:59:18.174Z').toLocaleString()}`, - }, - ]); - - expect(result.current.prompt.props['data-test-subj']).toEqual('prompt'); + it('should render action type selector', async () => { + const { getByTestId } = render(, { + wrapper: TestProviders, }); + + expect(getByTestId('modal-mock')).toBeInTheDocument(); }); - it('should set api config for each conversation when new connector is saved', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useConnectorSetup(defaultProps), { - wrapper: ({ children }) => {children}, - }); - await waitForNextUpdate(); - const { getByTestId, queryByTestId, rerender } = render(result.current.prompt, { - wrapper: TestProviders, - }); - expect(getByTestId('connectorButton')).toBeInTheDocument(); - expect(queryByTestId('skip-setup-button')).not.toBeInTheDocument(); - fireEvent.click(getByTestId('connectorButton')); - rerender(result.current.prompt); - fireEvent.click(getByTestId('modal-mock')); - expect(setApiConfig).toHaveBeenCalledTimes(1); + it('should set api config for each conversation when new connector is saved', async () => { + const { getByTestId } = render(, { + wrapper: TestProviders, }); + + fireEvent.click(getByTestId('modal-mock')); + expect(setApiConfig).toHaveBeenCalledTimes(1); }); it('should NOT set the api config for each conversation when a new connector is saved and updateConversationsOnSaveConnector is false', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => - useConnectorSetup({ - ...defaultProps, - updateConversationsOnSaveConnector: false, // <-- don't update the conversations - }), - { - wrapper: ({ children }) => {children}, - } - ); - await waitForNextUpdate(); - const { getByTestId, queryByTestId, rerender } = render(result.current.prompt, { + const { getByTestId } = render( + , + { wrapper: TestProviders, - }); - expect(getByTestId('connectorButton')).toBeInTheDocument(); - expect(queryByTestId('skip-setup-button')).not.toBeInTheDocument(); - fireEvent.click(getByTestId('connectorButton')); + } + ); - rerender(result.current.prompt); - fireEvent.click(getByTestId('modal-mock')); + fireEvent.click(getByTestId('modal-mock')); - expect(setApiConfig).not.toHaveBeenCalled(); - }); - }); - - it('should show skip button if message has presentation data', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook( - () => - useConnectorSetup({ - ...defaultProps, - conversation: { - ...defaultProps.conversation, - messages: [ - { - ...defaultProps.conversation.messages[0], - presentation: { - delay: 0, - stream: false, - }, - }, - ], - }, - }), - { - wrapper: ({ children }) => {children}, - } - ); - await waitForNextUpdate(); - const { getByTestId, queryByTestId } = render(result.current.prompt, { - wrapper: TestProviders, - }); - expect(getByTestId('skip-setup-button')).toBeInTheDocument(); - expect(queryByTestId('connectorButton')).not.toBeInTheDocument(); - }); - }); - it('should call onSetupComplete and setConversations when onHandleMessageStreamingComplete', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useConnectorSetup(defaultProps), { - wrapper: ({ children }) => {children}, - }); - await waitForNextUpdate(); - render(, { - wrapper: TestProviders, - }); - - expect(clearTimeout).toHaveBeenCalled(); - expect(onSetupComplete).toHaveBeenCalled(); - }); + expect(setApiConfig).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx index 81166bbf90fa1..a27da69709c38 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx @@ -5,181 +5,44 @@ * 2.0. */ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import type { EuiCommentProps } from '@elastic/eui'; -import { EuiAvatar, EuiBadge, EuiMarkdownFormat, EuiText, EuiTextAlign } from '@elastic/eui'; -import styled from '@emotion/styled'; -import { css } from '@emotion/react'; +import React, { useCallback, useMemo, useState } from 'react'; import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/common/constants'; import { ActionType } from '@kbn/triggers-actions-ui-plugin/public'; import { AddConnectorModal } from '../add_connector_modal'; import { WELCOME_CONVERSATION } from '../../assistant/use_conversation/sample_conversations'; -import { Conversation, ClientMessage } from '../../..'; +import { Conversation } from '../../..'; import { useLoadActionTypes } from '../use_load_action_types'; -import { StreamingText } from '../../assistant/streaming_text'; -import { ConnectorButton } from '../connector_button'; import { useConversation } from '../../assistant/use_conversation'; -import { conversationHasNoPresentationData } from './helpers'; -import * as i18n from '../translations'; import { useAssistantContext } from '../../assistant_context'; import { useLoadConnectors } from '../use_load_connectors'; -import { AssistantAvatar } from '../../assistant/assistant_avatar/assistant_avatar'; import { getGenAiConfig } from '../helpers'; -const ConnectorButtonWrapper = styled.div` - margin-bottom: 10px; -`; - export interface ConnectorSetupProps { conversation?: Conversation; - isFlyoutMode?: boolean; - onSetupComplete?: () => void; onConversationUpdate?: ({ cId, cTitle }: { cId: string; cTitle: string }) => Promise; updateConversationsOnSaveConnector?: boolean; } -export const useConnectorSetup = ({ +export const ConnectorSetup = ({ conversation: defaultConversation, - isFlyoutMode, - onSetupComplete, onConversationUpdate, updateConversationsOnSaveConnector = true, -}: ConnectorSetupProps): { - comments: EuiCommentProps[]; - prompt: React.ReactElement; -} => { +}: ConnectorSetupProps) => { const conversation = useMemo( - () => - defaultConversation || { - ...WELCOME_CONVERSATION, - messages: !isFlyoutMode ? WELCOME_CONVERSATION.messages : [], - }, - [defaultConversation, isFlyoutMode] + () => defaultConversation || WELCOME_CONVERSATION, + [defaultConversation] ); const { setApiConfig } = useConversation(); - const bottomRef = useRef(null); // Access all conversations so we can add connector to all on initial setup const { actionTypeRegistry, http } = useAssistantContext(); - const { - data: connectors, - isSuccess: areConnectorsFetched, - refetch: refetchConnectors, - } = useLoadConnectors({ http }); - const isConnectorConfigured = areConnectorsFetched && !!connectors?.length; + const { refetch: refetchConnectors } = useLoadConnectors({ http }); - const [isConnectorModalVisible, setIsConnectorModalVisible] = useState(false); - const [showAddConnectorButton, setShowAddConnectorButton] = useState(() => { - // If no presentation data on messages, default to showing add connector button so it doesn't delay render and flash on screen - return conversationHasNoPresentationData(conversation); - }); const { data: actionTypes } = useLoadActionTypes({ http }); const [selectedActionType, setSelectedActionType] = useState(null); - const lastConversationMessageIndex = useMemo( - () => conversation.messages.length - 1, - [conversation.messages.length] - ); - - const [currentMessageIndex, setCurrentMessageIndex] = useState( - // If connector is configured or conversation has already been replayed show all messages immediately - isConnectorConfigured || conversationHasNoPresentationData(conversation) - ? lastConversationMessageIndex - : 0 - ); - - const streamingTimeoutRef = useRef(undefined); - - // Once streaming of previous message is complete, proceed to next message - const onHandleMessageStreamingComplete = useCallback(() => { - if (currentMessageIndex === lastConversationMessageIndex) { - clearTimeout(streamingTimeoutRef.current); - return; - } - streamingTimeoutRef.current = window.setTimeout(() => { - bottomRef.current?.scrollIntoView({ block: 'end' }); - return setCurrentMessageIndex(currentMessageIndex + 1); - }, conversation.messages[currentMessageIndex]?.presentation?.delay ?? 0); - return () => clearTimeout(streamingTimeoutRef.current); - }, [conversation.messages, currentMessageIndex, lastConversationMessageIndex]); - - // Show button to add connector after last message has finished streaming - const onHandleLastMessageStreamingComplete = useCallback(() => { - setShowAddConnectorButton(true); - bottomRef.current?.scrollIntoView({ block: 'end' }); - onSetupComplete?.(); - }, [onSetupComplete]); - - // Show button to add connector after last message has finished streaming - const handleSkipSetup = useCallback(() => { - setCurrentMessageIndex(lastConversationMessageIndex); - }, [lastConversationMessageIndex]); - - // Create EuiCommentProps[] from conversation messages - const commentBody = useCallback( - (message: ClientMessage, index: number, length: number) => { - // If timestamp is not set, set it to current time (will update conversation at end of setup) - if ( - conversation.messages[index].timestamp == null || - conversation.messages[index].timestamp.length === 0 - ) { - conversation.messages[index].timestamp = new Date().toISOString(); - } - const isLastMessage = index === length - 1; - const enableStreaming = - (message?.presentation?.stream ?? false) && currentMessageIndex !== length - 1; - return ( - - {(streamedText, isStreamingComplete) => ( - - {streamedText} - - - )} - - ); - }, - [ - conversation.messages, - currentMessageIndex, - onHandleLastMessageStreamingComplete, - onHandleMessageStreamingComplete, - ] - ); - - const comments = useMemo( - () => - conversation.messages.slice(0, currentMessageIndex + 1).map((message, index) => { - const isUser = message.role === 'user'; - const timestamp = `${i18n.CONNECTOR_SETUP_TIMESTAMP_AT}: ${new Date( - message.timestamp - ).toLocaleString()}`; - const commentProps: EuiCommentProps = { - username: isUser ? i18n.CONNECTOR_SETUP_USER_YOU : i18n.CONNECTOR_SETUP_USER_ASSISTANT, - children: commentBody(message, index, conversation.messages.length), - timelineAvatar: ( - - ), - timestamp, - }; - return commentProps; - }), - [commentBody, conversation.messages, currentMessageIndex] - ); - const onSaveConnector = useCallback( async (connector: ActionConnector) => { if (updateConversationsOnSaveConnector) { @@ -204,7 +67,6 @@ export const useConnectorSetup = ({ }); refetchConnectors?.(); - setIsConnectorModalVisible(false); } } else { refetchConnectors?.(); @@ -221,65 +83,17 @@ export const useConnectorSetup = ({ const handleClose = useCallback(() => { setSelectedActionType(null); - setIsConnectorModalVisible(false); }, []); - return { - comments: isFlyoutMode ? [] : comments, - prompt: isFlyoutMode ? ( -

- -
- ) : ( -
- {showAddConnectorButton && ( - - - - )} - {!showAddConnectorButton && ( - - - - {i18n.CONNECTOR_SETUP_SKIP} - - - - )} - {isConnectorModalVisible && ( - - )} -
- ), - }; + return ( + + ); }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/content/prompts/welcome/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/content/prompts/welcome/translations.ts index 387c1d01422f6..3324d09b50a5a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/content/prompts/welcome/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/content/prompts/welcome/translations.ts @@ -7,30 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const WELCOME_GENERAL = i18n.translate( - 'xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneralPrompt', - { - defaultMessage: - 'Welcome to your Elastic AI Assistant! I am your 100% open-code portal into your Elastic life. In time, I will be able to answer questions and provide assistance across all your information in Elastic, and oh-so much more. Till then, I hope this early preview will open your mind to the possibilities of what we can create when we work together, in the open. Cheers!', - } -); - -export const WELCOME_GENERAL_2 = i18n.translate( - 'xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral2Prompt', - { - defaultMessage: - "First things first, we'll need to set up a Generative AI Connector to get this chat experience going! With the Generative AI Connector, you'll be able to configure access to either an OpenAI service or an Amazon Bedrock service, but you better believe you'll be able to deploy your own models within your Elastic Cloud instance and use those here in the future... 😉", - } -); - -export const WELCOME_GENERAL_3 = i18n.translate( - 'xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral3Prompt', - { - defaultMessage: - 'Go ahead and click the add connector button below to continue the conversation!', - } -); - export const ENTERPRISE = i18n.translate( 'xpack.elasticAssistant.securityAssistant.content.prompts.welcome.enterprisePrompt', { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/context_preview.tsx b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/context_preview.tsx index 91b676c491e47..49d5d0fe4d63d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/context_preview.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/context_preview.tsx @@ -48,6 +48,7 @@ const SelectedPromptContextPreviewComponent = ({ return ( { rawData: 'test-raw-data', }; - it('renders stats', () => { - render( - - - - ); - - expect(screen.getByTestId('stats')).toBeInTheDocument(); - }); - describe('when rawData is a string (non-anonymized data)', () => { it('renders the ReadOnlyContextViewer when rawData is (non-anonymized data)', () => { render( @@ -61,7 +45,6 @@ describe('DataAnonymizationEditor', () => { selectedPromptContext={mockSelectedPromptContext} setSelectedPromptContexts={jest.fn()} currentReplacements={{}} - isFlyoutMode={false} /> ); @@ -76,7 +59,6 @@ describe('DataAnonymizationEditor', () => { selectedPromptContext={mockSelectedPromptContext} setSelectedPromptContexts={jest.fn()} currentReplacements={{}} - isFlyoutMode={false} /> ); @@ -105,24 +87,17 @@ describe('DataAnonymizationEditor', () => { selectedPromptContext={selectedPromptContextWithAnonymized} setSelectedPromptContexts={setSelectedPromptContexts} currentReplacements={{}} - isFlyoutMode={false} /> ); }); - it('renders the ContextEditor when rawData is anonymized data', () => { - expect(screen.getByTestId('contextEditor')).toBeInTheDocument(); + it('renders the SelectedPromptContextPreview when rawData is anonymized data', () => { + expect(screen.getByTestId('selectedPromptContextPreview')).toBeInTheDocument(); }); it('does NOT render the ReadOnlyContextViewer when rawData is anonymized data', () => { expect(screen.queryByTestId('readOnlyContextViewer')).not.toBeInTheDocument(); }); - - it('calls setSelectedPromptContexts when a field is toggled', () => { - userEvent.click(screen.getAllByTestId('allowed')[0]); // toggle the first field - - expect(setSelectedPromptContexts).toBeCalled(); - }); }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/index.tsx index 1fd0e31c78767..0794ca4330350 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization_editor/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiPanel, EuiSpacer } from '@elastic/eui'; +import { EuiPanel } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import styled from '@emotion/styled'; import { AnonymizedData } from '@kbn/elastic-assistant-common/impl/data_anonymization/types'; @@ -14,9 +14,7 @@ import { BatchUpdateListItem } from './context_editor/types'; import { getIsDataAnonymizable, updateSelectedPromptContext } from './helpers'; import { ReadOnlyContextViewer } from './read_only_context_viewer'; import { ContextEditorFlyout } from './context_editor_flyout'; -import { ContextEditor } from './context_editor'; import { ReplacementsContextViewer } from './replacements_context_viewer'; -import { Stats } from './stats'; const EditorContainer = styled.div` overflow-x: auto; @@ -28,14 +26,12 @@ export interface Props { React.SetStateAction> >; currentReplacements: AnonymizedData['replacements'] | undefined; - isFlyoutMode: boolean; } const DataAnonymizationEditorComponent: React.FC = ({ selectedPromptContext, setSelectedPromptContexts, currentReplacements, - isFlyoutMode, }) => { const isDataAnonymizable = useMemo( () => getIsDataAnonymizable(selectedPromptContext.rawData), @@ -63,66 +59,27 @@ const DataAnonymizationEditorComponent: React.FC = ({ [selectedPromptContext, setSelectedPromptContexts] ); - if (isFlyoutMode) { - return ( - - - {typeof selectedPromptContext.rawData === 'string' ? ( - selectedPromptContext.replacements != null ? ( - - ) : ( - - ) - ) : ( - - )} - - - ); - } - return ( - - - - - {typeof selectedPromptContext.rawData === 'string' ? ( - selectedPromptContext.replacements != null ? ( - + + {typeof selectedPromptContext.rawData === 'string' ? ( + selectedPromptContext.replacements != null ? ( + + ) : ( + + ) ) : ( - - ) - ) : ( - - )} + + )} + ); }; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/mock/get_anonymized_value/index.ts b/x-pack/packages/kbn-elastic-assistant/impl/mock/get_anonymized_value/index.ts index a6d5c4e5d3972..256f9776c4563 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/mock/get_anonymized_value/index.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/mock/get_anonymized_value/index.ts @@ -5,13 +5,6 @@ * 2.0. */ -import { Replacements } from '@kbn/elastic-assistant-common'; - /** This mock returns the reverse of `value` */ -export const mockGetAnonymizedValue = ({ - currentReplacements, - rawValue, -}: { - currentReplacements: Replacements | undefined; - rawValue: string; -}): string => rawValue.split('').reverse().join(''); +export const mockGetAnonymizedValue = ({ rawValue }: { rawValue: string }): string => + rawValue.split('').reverse().join(''); diff --git a/x-pack/packages/kbn-entities-schema/src/schema/common.ts b/x-pack/packages/kbn-entities-schema/src/schema/common.ts index b0d4b7247d12c..bb9d07b1957f4 100644 --- a/x-pack/packages/kbn-entities-schema/src/schema/common.ts +++ b/x-pack/packages/kbn-entities-schema/src/schema/common.ts @@ -96,3 +96,9 @@ export const identityFieldsSchema = z optional: z.boolean(), }) .or(z.string().transform((value) => ({ field: value, optional: false }))); + +const semVerRegex = new RegExp(/^[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}$/); +export const semVerSchema = z.string().refine((maybeSemVer) => semVerRegex.test(maybeSemVer), { + message: + 'The string does use the Semantic Versioning (Semver) format of {major}.{minor}.{patch} (e.g., 1.0.0), ensure each part contains only digits.', +}); diff --git a/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts b/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts index 15f3e98582c97..8fee16117b9c6 100644 --- a/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts +++ b/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts @@ -13,10 +13,12 @@ import { filterSchema, durationSchema, identityFieldsSchema, + semVerSchema, } from './common'; export const entityDefinitionSchema = z.object({ id: z.string().regex(/^[\w-]+$/), + version: semVerSchema, name: z.string(), description: z.optional(z.string()), type: z.string(), diff --git a/x-pack/packages/ml/inference_integration_flyout/components/elasticsearch_models.tsx b/x-pack/packages/ml/inference_integration_flyout/components/elasticsearch_models.tsx index 0825cba6828b4..4bd6354ef73da 100644 --- a/x-pack/packages/ml/inference_integration_flyout/components/elasticsearch_models.tsx +++ b/x-pack/packages/ml/inference_integration_flyout/components/elasticsearch_models.tsx @@ -70,10 +70,7 @@ export const ElasticsearchModels: React.FC = ({ } }, [numberOfAllocations, numberOfThreads, serviceType]); - const elasticSearchModelTypesDescriptions: Record< - ElasticsearchModelDefaultOptions | string, - ElasticsearchModelDescriptions - > = { + const elasticSearchModelTypesDescriptions: Record = { [ElasticsearchModelDefaultOptions.elser]: { description: i18n.translate( 'xpack.ml.addInferenceEndpoint.elasticsearchModels.elser.description', diff --git a/x-pack/packages/ml/trained_models_utils/index.ts b/x-pack/packages/ml/trained_models_utils/index.ts index ada09d62af072..293f6662f5011 100644 --- a/x-pack/packages/ml/trained_models_utils/index.ts +++ b/x-pack/packages/ml/trained_models_utils/index.ts @@ -27,4 +27,13 @@ export { ELASTIC_MODEL_TYPE, MODEL_STATE, type ModelState, + ELSER_LINUX_OPTIMIZED_MODEL_ID, + ELSER_MODEL_ID, + E5_LINUX_OPTIMIZED_MODEL_ID, + E5_MODEL_ID, + LANG_IDENT_MODEL_ID, + LATEST_ELSER_VERSION, + LATEST_ELSER_MODEL_ID, + LATEST_E5_MODEL_ID, + ElserModels, } from './src/constants/trained_models'; diff --git a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts index 0e19fef36e2e2..938f9c0ef81e0 100644 --- a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts +++ b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts @@ -7,6 +7,18 @@ import { i18n } from '@kbn/i18n'; +export const ELSER_MODEL_ID = '.elser_model_2'; +export const ELSER_LINUX_OPTIMIZED_MODEL_ID = '.elser_model_2_linux-x86_64'; +export const E5_MODEL_ID = '.multilingual-e5-small'; +export const E5_LINUX_OPTIMIZED_MODEL_ID = '.multilingual-e5-small_linux-x86_64'; +export const LANG_IDENT_MODEL_ID = 'lang_ident_model_1'; +export const ELSER_ID_V1 = '.elser_model_1' as const; +export const LATEST_ELSER_VERSION: ElserVersion = 2; +export const LATEST_ELSER_MODEL_ID = ELSER_LINUX_OPTIMIZED_MODEL_ID; +export const LATEST_E5_MODEL_ID = E5_LINUX_OPTIMIZED_MODEL_ID; + +export const ElserModels = [ELSER_MODEL_ID, ELSER_LINUX_OPTIMIZED_MODEL_ID, ELSER_ID_V1]; + export const DEPLOYMENT_STATE = { STARTED: 'started', STARTING: 'starting', @@ -46,10 +58,8 @@ export const BUILT_IN_MODEL_TAG = 'prepackaged'; export const ELASTIC_MODEL_TAG = 'elastic'; -export const ELSER_ID_V1 = '.elser_model_1' as const; - export const ELASTIC_MODEL_DEFINITIONS: Record = Object.freeze({ - '.elser_model_1': { + [ELSER_ID_V1]: { modelName: 'elser', hidden: true, version: 1, @@ -63,7 +73,7 @@ export const ELASTIC_MODEL_DEFINITIONS: Record = Object }), type: ['elastic', 'pytorch', 'text_expansion'], }, - '.elser_model_2': { + [ELSER_MODEL_ID]: { modelName: 'elser', version: 2, default: true, @@ -77,7 +87,7 @@ export const ELASTIC_MODEL_DEFINITIONS: Record = Object }), type: ['elastic', 'pytorch', 'text_expansion'], }, - '.elser_model_2_linux-x86_64': { + [ELSER_LINUX_OPTIMIZED_MODEL_ID]: { modelName: 'elser', version: 2, os: 'Linux', @@ -92,7 +102,7 @@ export const ELASTIC_MODEL_DEFINITIONS: Record = Object }), type: ['elastic', 'pytorch', 'text_expansion'], }, - '.multilingual-e5-small': { + [E5_MODEL_ID]: { modelName: 'e5', version: 1, default: true, @@ -108,7 +118,7 @@ export const ELASTIC_MODEL_DEFINITIONS: Record = Object licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], }, - '.multilingual-e5-small_linux-x86_64': { + [E5_LINUX_OPTIMIZED_MODEL_ID]: { modelName: 'e5', version: 1, os: 'Linux', @@ -178,29 +188,69 @@ export interface GetModelDownloadConfigOptions { version?: ElserVersion; } +export interface LocalInferenceServiceSettings { + service: 'elser' | 'elasticsearch'; + service_settings: { + num_allocations: number; + num_threads: number; + model_id: string; + }; +} + export type InferenceServiceSettings = + | LocalInferenceServiceSettings | { - service: 'elser'; + service: 'openai'; service_settings: { - num_allocations: number; - num_threads: number; + api_key: string; + organization_id: string; + url: string; model_id: string; }; } | { - service: 'elasticsearch'; + service: 'mistral'; + service_settings: { + api_key: string; + model: string; + max_input_tokens: string; + rate_limit: { + requests_per_minute: number; + }; + }; + } + | { + service: 'cohere'; service_settings: { - num_allocations: number; - num_threads: number; + similarity: string; + dimensions: string; model_id: string; + embedding_type: string; }; } | { - service: 'openai'; + service: 'azureaistudio'; service_settings: { - api_key: string; - organization_id: string; - url: string; + target: string; + provider: string; + embedding_type: string; + }; + } + | { + service: 'azureopenai'; + service_settings: { + resource_name: string; + deployment_id: string; + api_version: string; + }; + } + | { + service: 'googleaistudio'; + service_settings: { + model_id: string; + rate_limit: { + requests_per_minute: number; + }; }; } | { diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.ts index 5b3860af44920..3eaf493222cb0 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/body/data_quality_details/storage_details/helpers.ts @@ -134,37 +134,34 @@ export const getFlattenedBuckets = ({ if (((isILMAvailable && ilmExplain != null) || !isILMAvailable) && stats != null) { return [ ...acc, - ...Object.entries(stats).reduce( - (validStats, [indexName, indexStats]) => { - const ilmPhase = getIlmPhase(ilmExplain?.[indexName], isILMAvailable); - const isSelectedPhase = - (isILMAvailable && ilmPhase != null && ilmPhasesMap[ilmPhase] != null) || - !isILMAvailable; - - if (isSelectedPhase) { - const incompatible = - results != null && results[indexName] != null - ? results[indexName].incompatible - : undefined; - const sizeInBytes = getSizeInBytes({ indexName, stats }); - const docsCount = getDocsCount({ stats, indexName }); - return [ - ...validStats, - { - ilmPhase, - incompatible, - indexName, - pattern, - sizeInBytes, - docsCount, - }, - ]; - } else { - return validStats; - } - }, - [] - ), + ...Object.entries(stats).reduce((validStats, [indexName]) => { + const ilmPhase = getIlmPhase(ilmExplain?.[indexName], isILMAvailable); + const isSelectedPhase = + (isILMAvailable && ilmPhase != null && ilmPhasesMap[ilmPhase] != null) || + !isILMAvailable; + + if (isSelectedPhase) { + const incompatible = + results != null && results[indexName] != null + ? results[indexName].incompatible + : undefined; + const sizeInBytes = getSizeInBytes({ indexName, stats }); + const docsCount = getDocsCount({ stats, indexName }); + return [ + ...validStats, + { + ilmPhase, + incompatible, + indexName, + pattern, + sizeInBytes, + docsCount, + }, + ]; + } else { + return validStats; + } + }, []), ]; } @@ -232,7 +229,7 @@ export const getLayersMultiDimensional = ({ groupByRollup: (d: Datum) => d.indexName, nodeLabel: (indexName: Datum) => indexName, shape: { - fillColor: (indexName: Key, sortIndex: number, node: Pick) => { + fillColor: (indexName: Key, _sortIndex: number, node: Pick) => { const pattern = getGroupFromPath(node.path) ?? ''; const flattenedBucket = pathToFlattenedBucketMap[`${pattern}${indexName}`]; diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/stat_label/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/stat_label/index.tsx index 31b4620fbb5f0..32402b49b570c 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/stat_label/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/stat_label/index.tsx @@ -19,12 +19,11 @@ const Line2 = styled.span` const EMPTY = ' '; interface Props { - color?: string; line1?: string; line2?: string; } -export const StatLabel: React.FC = ({ color, line1 = EMPTY, line2 = EMPTY }) => ( +export const StatLabel: React.FC = ({ line1 = EMPTY, line2 = EMPTY }) => ( <> {line1} {line2} diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/ecs_compliant_tab/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/ecs_compliant_tab/index.tsx index b53567e709eb4..855ef75e80b84 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/ecs_compliant_tab/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/ecs_compliant_tab/index.tsx @@ -26,15 +26,10 @@ const EmptyPromptContainer = styled.div` interface Props { indexName: string; - onAddToNewCase: (markdownComments: string[]) => void; partitionedFieldMetadata: PartitionedFieldMetadata; } -const EcsCompliantTabComponent: React.FC = ({ - indexName, - onAddToNewCase, - partitionedFieldMetadata, -}) => { +const EcsCompliantTabComponent: React.FC = ({ indexName, partitionedFieldMetadata }) => { const emptyPromptBody = useMemo(() => , []); const title = useMemo(() => , []); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx index 670357c3730f7..8790ab12591b3 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/helpers.tsx @@ -212,11 +212,7 @@ export const getTabs = ({
), content: ( - + ), id: ECS_COMPLIANT_TAB_ID, name: i18n.ECS_COMPLIANT_FIELDS, diff --git a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts index ca0f592f96a43..f5af4cd05ad24 100644 --- a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts +++ b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.styles.ts @@ -69,7 +69,7 @@ export const SolutionSideNavCategoryTitleStyles = (euiTheme: EuiThemeComputed<{} font-weight: ${euiTheme.font.weight.medium}; `; -export const SolutionSideNavPanelLinksGroupStyles = (euiTheme: EuiThemeComputed<{}>) => css` +export const SolutionSideNavPanelLinksGroupStyles = () => css` padding-left: 0; padding-right: 0; `; diff --git a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx index dfe2f643d4783..248121872018b 100644 --- a/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx +++ b/x-pack/packages/security-solution/side_nav/src/solution_side_nav_panel.tsx @@ -333,8 +333,7 @@ interface SolutionSideNavPanelItemsProps { */ const SolutionSideNavPanelItems: React.FC = React.memo( function SolutionSideNavPanelItems({ items, onClose }) { - const { euiTheme } = useEuiTheme(); - const panelLinksGroupClassNames = classNames(SolutionSideNavPanelLinksGroupStyles(euiTheme)); + const panelLinksGroupClassNames = classNames(SolutionSideNavPanelLinksGroupStyles()); return ( {items.map((item) => ( diff --git a/x-pack/packages/security/plugin_types_server/index.ts b/x-pack/packages/security/plugin_types_server/index.ts index 1228b9d36f961..21ab0eb2b39af 100644 --- a/x-pack/packages/security/plugin_types_server/index.ts +++ b/x-pack/packages/security/plugin_types_server/index.ts @@ -14,15 +14,6 @@ export type { AuditLogger, } from './src/audit'; export type { - CreateAPIKeyParams, - CreateAPIKeyResult, - CreateRestAPIKeyParams, - GrantAPIKeyResult, - InvalidateAPIKeysParams, - ValidateAPIKeyParams, - CreateRestAPIKeyWithKibanaPrivilegesParams, - CreateCrossClusterAPIKeyParams, - InvalidateAPIKeyResult, APIKeys, AuthenticationServiceStart, UpdateAPIKeyParams, @@ -39,7 +30,6 @@ export type { CheckPrivilegesWithRequest, CheckSavedObjectsPrivilegesWithRequest, CheckPrivilegesDynamicallyWithRequest, - KibanaPrivilegesType, SavedObjectActions, UIActions, CheckPrivilegesPayload, @@ -51,7 +41,6 @@ export type { CheckPrivilegesOptions, CheckUserProfilesPrivilegesPayload, CheckUserProfilesPrivilegesResponse, - ElasticsearchPrivilegesType, CasesActions, CheckPrivileges, AlertingActions, @@ -72,11 +61,30 @@ export type { } from './src/user_profile'; export { - restApiKeySchema, - getRestApiKeyWithKibanaPrivilegesSchema, getUpdateRestApiKeyWithKibanaPrivilegesSchema, - crossClusterApiKeySchema, updateRestApiKeySchema, updateCrossClusterApiKeySchema, } from './src/authentication'; -export { GLOBAL_RESOURCE, elasticsearchRoleSchema, getKibanaRoleSchema } from './src/authorization'; + +export type { + ElasticsearchPrivilegesType, + KibanaPrivilegesType, + APIKeysService, + CreateAPIKeyParams, + CreateAPIKeyResult, + InvalidateAPIKeyResult, + InvalidateAPIKeysParams, + ValidateAPIKeyParams, + CreateRestAPIKeyParams, + CreateRestAPIKeyWithKibanaPrivilegesParams, + CreateCrossClusterAPIKeyParams, + GrantAPIKeyResult, +} from '@kbn/core-security-server'; +export { isCreateRestAPIKeyParams } from '@kbn/core-security-server'; + +export { + restApiKeySchema, + crossClusterApiKeySchema, + getRestApiKeyWithKibanaPrivilegesSchema, +} from './src/authentication'; +export { getKibanaRoleSchema, elasticsearchRoleSchema, GLOBAL_RESOURCE } from './src/authorization'; diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts index 2ced5478b46eb..c331802c7f693 100644 --- a/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts +++ b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/api_keys.ts @@ -5,153 +5,9 @@ * 2.0. */ -import type { estypes } from '@elastic/elasticsearch'; - -import type { KibanaRequest } from '@kbn/core/server'; -import { schema, TypeOf } from '@kbn/config-schema'; +import { schema } from '@kbn/config-schema'; import { getKibanaRoleSchema, elasticsearchRoleSchema } from '../../authorization'; -export interface APIKeys { - /** - * Determines if API Keys are enabled in Elasticsearch. - */ - areAPIKeysEnabled(): Promise; - - /** - * Determines if Cross-Cluster API Keys are enabled in Elasticsearch. - */ - areCrossClusterAPIKeysEnabled(): Promise; - - /** - * Tries to create an API key for the current user. - * - * Returns newly created API key or `null` if API keys are disabled. - * - * User needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys. - * - * @param request Request instance. - * @param createParams The params to create an API key - */ - create( - request: KibanaRequest, - createParams: CreateAPIKeyParams - ): Promise; - - /** - * Tries to grant an API key for the current user. - * @param request Request instance. - * @param createParams Create operation parameters. - */ - grantAsInternalUser( - request: KibanaRequest, - createParams: CreateRestAPIKeyParams | CreateRestAPIKeyWithKibanaPrivilegesParams - ): Promise; - - /** - * Tries to validate an API key. - * @param apiKeyPrams ValidateAPIKeyParams. - */ - validate(apiKeyPrams: ValidateAPIKeyParams): Promise; - - /** - * Tries to invalidate an API keys. - * @param request Request instance. - * @param params The params to invalidate an API keys. - */ - invalidate( - request: KibanaRequest, - params: InvalidateAPIKeysParams - ): Promise; - - /** - * Tries to invalidate the API keys by using the internal user. - * @param params The params to invalidate the API keys. - */ - invalidateAsInternalUser(params: InvalidateAPIKeysParams): Promise; -} - -export type CreateAPIKeyParams = - | CreateRestAPIKeyParams - | CreateRestAPIKeyWithKibanaPrivilegesParams - | CreateCrossClusterAPIKeyParams; - -/** - * Response of Kibana Create API key endpoint. - */ -export type CreateAPIKeyResult = estypes.SecurityCreateApiKeyResponse; - -export type CreateRestAPIKeyParams = TypeOf; -export type CreateRestAPIKeyWithKibanaPrivilegesParams = TypeOf< - ReturnType ->; -export type CreateCrossClusterAPIKeyParams = TypeOf; - -export interface GrantAPIKeyResult { - /** - * Unique id for this API key - */ - id: string; - /** - * Name for this API key - */ - name: string; - /** - * Generated API key - */ - api_key: string; -} - -/** - * Represents the parameters for validating API Key credentials. - */ -export interface ValidateAPIKeyParams { - /** - * Unique id for this API key - */ - id: string; - - /** - * Generated API Key (secret) - */ - api_key: string; -} - -/** - * Represents the params for invalidating multiple API keys - */ -export interface InvalidateAPIKeysParams { - ids: string[]; -} - -/** - * The return value when invalidating an API key in Elasticsearch. - */ -export interface InvalidateAPIKeyResult { - /** - * The IDs of the API keys that were invalidated as part of the request. - */ - invalidated_api_keys: string[]; - /** - * The IDs of the API keys that were already invalidated. - */ - previously_invalidated_api_keys: string[]; - /** - * The number of errors that were encountered when invalidating the API keys. - */ - error_count: number; - /** - * Details about these errors. This field is not present in the response when error_count is 0. - */ - error_details?: Array<{ - type?: string; - reason?: string; - caused_by?: { - type?: string; - reason?: string; - }; - }>; -} - export const restApiKeySchema = schema.object({ type: schema.maybe(schema.literal('rest')), name: schema.string(), @@ -165,8 +21,11 @@ export const restApiKeySchema = schema.object({ export const getRestApiKeyWithKibanaPrivilegesSchema = ( getBasePrivilegeNames: Parameters[0] ) => - restApiKeySchema.extends({ - role_descriptors: null, + schema.object({ + type: schema.maybe(schema.literal('rest')), + name: schema.string(), + expiration: schema.maybe(schema.string()), + metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })), kibana_role_descriptors: schema.recordOf( schema.string(), schema.object({ @@ -176,9 +35,11 @@ export const getRestApiKeyWithKibanaPrivilegesSchema = ( ), }); -export const crossClusterApiKeySchema = restApiKeySchema.extends({ +export const crossClusterApiKeySchema = schema.object({ type: schema.literal('cross_cluster'), - role_descriptors: null, + name: schema.string(), + expiration: schema.maybe(schema.string()), + metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })), access: schema.object( { search: schema.maybe( @@ -203,41 +64,52 @@ export const crossClusterApiKeySchema = restApiKeySchema.extends({ ), }); -/** - * Response of Kibana Update API key endpoint. - */ -export type UpdateAPIKeyResult = estypes.SecurityUpdateApiKeyResponse; - -/** - * Request body of Kibana Update API key endpoint. - */ -export type UpdateAPIKeyParams = - | UpdateRestAPIKeyParams - | UpdateCrossClusterAPIKeyParams - | UpdateRestAPIKeyWithKibanaPrivilegesParams; - -export const updateRestApiKeySchema = restApiKeySchema.extends({ - name: null, +export const updateRestApiKeySchema = schema.object({ id: schema.string(), + type: schema.maybe(schema.literal('rest')), + expiration: schema.maybe(schema.string()), + role_descriptors: schema.recordOf(schema.string(), schema.object({}, { unknowns: 'allow' }), { + defaultValue: {}, + }), + metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })), }); -export const updateCrossClusterApiKeySchema = crossClusterApiKeySchema.extends({ - name: null, +export const updateCrossClusterApiKeySchema = schema.object({ id: schema.string(), + type: schema.literal('cross_cluster'), + expiration: schema.maybe(schema.string()), + metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })), + access: schema.object( + { + search: schema.maybe( + schema.arrayOf( + schema.object({ + names: schema.arrayOf(schema.string()), + query: schema.maybe(schema.any()), + field_security: schema.maybe(schema.any()), + allow_restricted_indices: schema.maybe(schema.boolean()), + }) + ) + ), + replication: schema.maybe( + schema.arrayOf( + schema.object({ + names: schema.arrayOf(schema.string()), + }) + ) + ), + }, + { unknowns: 'allow' } + ), }); -export type UpdateRestAPIKeyParams = TypeOf; -export type UpdateCrossClusterAPIKeyParams = TypeOf; -export type UpdateRestAPIKeyWithKibanaPrivilegesParams = TypeOf< - ReturnType ->; - export const getUpdateRestApiKeyWithKibanaPrivilegesSchema = ( getBasePrivilegeNames: Parameters[0] ) => - restApiKeySchema.extends({ - role_descriptors: null, - name: null, + schema.object({ + type: schema.maybe(schema.literal('rest')), + expiration: schema.maybe(schema.string()), + metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })), id: schema.string(), kibana_role_descriptors: schema.recordOf( schema.string(), diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/index.ts b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/index.ts index ec36a99b4da63..1673682052554 100644 --- a/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/index.ts +++ b/x-pack/packages/security/plugin_types_server/src/authentication/api_keys/index.ts @@ -5,23 +5,6 @@ * 2.0. */ -export type { - CreateAPIKeyParams, - CreateAPIKeyResult, - InvalidateAPIKeyResult, - InvalidateAPIKeysParams, - ValidateAPIKeyParams, - CreateRestAPIKeyParams, - CreateRestAPIKeyWithKibanaPrivilegesParams, - CreateCrossClusterAPIKeyParams, - GrantAPIKeyResult, - APIKeys, - UpdateAPIKeyParams, - UpdateAPIKeyResult, - UpdateCrossClusterAPIKeyParams, - UpdateRestAPIKeyParams, - UpdateRestAPIKeyWithKibanaPrivilegesParams, -} from './api_keys'; export { crossClusterApiKeySchema, getRestApiKeyWithKibanaPrivilegesSchema, diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts b/x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts index 6bc5a73113ae3..5d066bb6565ca 100644 --- a/x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts +++ b/x-pack/packages/security/plugin_types_server/src/authentication/authentication_service.ts @@ -6,14 +6,13 @@ */ import type { KibanaRequest } from '@kbn/core/server'; -import type { AuthenticatedUser } from '@kbn/core-security-common'; - -import type { APIKeys } from './api_keys'; +import type { AuthenticatedUser } from '@kbn/security-plugin-types-common'; +import type { APIKeysService } from '@kbn/core-security-server'; /** * Authentication services available on the security plugin's start contract. */ export interface AuthenticationServiceStart { - apiKeys: APIKeys; + apiKeys: APIKeysService; getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; } diff --git a/x-pack/packages/security/plugin_types_server/src/authentication/index.ts b/x-pack/packages/security/plugin_types_server/src/authentication/index.ts index 6e30f9ebcec24..4a5e7da782baf 100644 --- a/x-pack/packages/security/plugin_types_server/src/authentication/index.ts +++ b/x-pack/packages/security/plugin_types_server/src/authentication/index.ts @@ -5,29 +5,22 @@ * 2.0. */ +export type { AuthenticationServiceStart } from './authentication_service'; + export type { - CreateAPIKeyParams, - CreateAPIKeyResult, - CreateRestAPIKeyParams, - CreateRestAPIKeyWithKibanaPrivilegesParams, - CreateCrossClusterAPIKeyParams, - InvalidateAPIKeyResult, - InvalidateAPIKeysParams, - ValidateAPIKeyParams, - APIKeys, - GrantAPIKeyResult, + APIKeysService as APIKeys, UpdateAPIKeyParams, UpdateAPIKeyResult, UpdateCrossClusterAPIKeyParams, UpdateRestAPIKeyParams, UpdateRestAPIKeyWithKibanaPrivilegesParams, -} from './api_keys'; -export type { AuthenticationServiceStart } from './authentication_service'; +} from '@kbn/core-security-server'; + export { - restApiKeySchema, + crossClusterApiKeySchema, getRestApiKeyWithKibanaPrivilegesSchema, getUpdateRestApiKeyWithKibanaPrivilegesSchema, - crossClusterApiKeySchema, + restApiKeySchema, updateRestApiKeySchema, updateCrossClusterApiKeySchema, } from './api_keys'; diff --git a/x-pack/packages/security/plugin_types_server/src/authorization/index.ts b/x-pack/packages/security/plugin_types_server/src/authorization/index.ts index 54364d7817f31..baeeeddc1fa74 100644 --- a/x-pack/packages/security/plugin_types_server/src/authorization/index.ts +++ b/x-pack/packages/security/plugin_types_server/src/authorization/index.ts @@ -42,7 +42,6 @@ export type { PrivilegeDeprecationsRolesByFeatureIdResponse, } from './deprecations'; export type { AuthorizationMode } from './mode'; -export type { ElasticsearchPrivilegesType, KibanaPrivilegesType } from './role_schema'; export { GLOBAL_RESOURCE } from './constants'; export { elasticsearchRoleSchema, getKibanaRoleSchema } from './role_schema'; diff --git a/x-pack/packages/security/plugin_types_server/tsconfig.json b/x-pack/packages/security/plugin_types_server/tsconfig.json index 04ed00229a3de..2f4ae387ac2b5 100644 --- a/x-pack/packages/security/plugin_types_server/tsconfig.json +++ b/x-pack/packages/security/plugin_types_server/tsconfig.json @@ -10,11 +10,10 @@ "target/**/*" ], "kbn_references": [ - "@kbn/config-schema", "@kbn/core", "@kbn/security-plugin-types-common", "@kbn/core-user-profile-server", "@kbn/core-security-server", - "@kbn/core-security-common" + "@kbn/config-schema", ] } diff --git a/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap b/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap index e1366f7f9c573..05d74f781c434 100644 --- a/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap +++ b/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap @@ -7561,6 +7561,44 @@ Object { ], "type": "alternatives", }, + "scriptType": Object { + "flags": Object { + "default": null, + "error": [Function], + "presence": "optional", + }, + "matches": Array [ + Object { + "schema": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + Object { + "schema": Object { + "allow": Array [ + null, + ], + "flags": Object { + "error": [Function], + "only": true, + }, + "type": "any", + }, + }, + ], + "type": "alternatives", + }, }, "type": "object", } diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index b1d62c4275180..7d55c3098cfaf 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -41,7 +41,9 @@ export class AiopsPlugin { registerChangePointChartsAttachment }, [coreStart, pluginStart], ]) => { - if (license.hasAtLeast('platinum')) { + const { canUseAiops } = coreStart.application.capabilities.ml; + + if (license.hasAtLeast('platinum') && canUseAiops) { if (embeddable) { registerEmbeddables(embeddable, core); } diff --git a/x-pack/plugins/cases/docs/openapi/bundled.yaml b/x-pack/plugins/cases/docs/openapi/bundled.yaml index 08032c30e7f50..7aeea6de8e052 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.yaml +++ b/x-pack/plugins/cases/docs/openapi/bundled.yaml @@ -281,7 +281,6 @@ paths: - elastic customFields: type: array - x-technical-preview: true description: Custom fields configuration details. items: type: object @@ -478,7 +477,6 @@ paths: - elastic customFields: type: array - x-technical-preview: true description: Custom fields configuration details. items: type: object @@ -677,7 +675,6 @@ paths: - elastic customFields: type: array - x-technical-preview: true description: Custom fields configuration details. items: type: object @@ -1548,7 +1545,6 @@ paths: - elastic customFields: type: array - x-technical-preview: true description: Custom fields configuration details. items: type: object @@ -1746,7 +1742,6 @@ paths: - elastic customFields: type: array - x-technical-preview: true description: Custom fields configuration details. items: type: object @@ -1946,7 +1941,6 @@ paths: - elastic customFields: type: array - x-technical-preview: true description: Custom fields configuration details. items: type: object @@ -3305,7 +3299,6 @@ components: type: array description: | Custom field values for a case. Any optional custom fields that are not specified in the request are set to null. - x-technical-preview: true minItems: 0 maxItems: 10 items: @@ -3804,7 +3797,6 @@ components: customFields: type: array description: Custom field values for the case. - x-technical-preview: true items: type: object properties: @@ -3933,7 +3925,6 @@ components: type: array description: | Custom field values for a case. Any optional custom fields that are not specified in the request are set to null. - x-technical-preview: true minItems: 0 maxItems: 10 items: @@ -4053,7 +4044,6 @@ components: $ref: '#/components/schemas/connector_types' customFields: type: array - x-technical-preview: true description: Custom field values in the template. items: type: object @@ -4135,7 +4125,6 @@ components: customFields: type: array description: Custom fields case configuration. - x-technical-preview: true minItems: 0 maxItems: 10 items: @@ -4216,7 +4205,6 @@ components: customFields: type: array description: Custom fields case configuration. - x-technical-preview: true items: type: object required: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml index 26a7ed41685a1..1085e1f8ef974 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_configure_response_properties.yaml @@ -1,6 +1,6 @@ closure_type: $ref: 'closure_types.yaml' -connector: +connector: type: object properties: $ref: 'case_configure_connector_properties.yaml' @@ -12,14 +12,13 @@ created_at: created_by: type: object required: - - email - - full_name - - username + - email + - full_name + - username properties: $ref: 'user_properties.yaml' customFields: type: array - x-technical-preview: true description: Custom fields configuration details. items: type: object @@ -27,8 +26,8 @@ customFields: $ref: 'case_configure_customfields.yaml' error: type: - - "string" - - "null" + - 'string' + - 'null' examples: - null id: @@ -58,22 +57,22 @@ templates: $ref: 'templates.yaml' updated_at: type: - - "string" - - "null" + - 'string' + - 'null' format: date-time examples: - 2022-06-01T19:58:48.169Z updated_by: type: - - "object" - - "null" + - 'object' + - 'null' required: - - email - - full_name - - username + - email + - full_name + - username properties: $ref: 'user_properties.yaml' version: type: string examples: - - WzIwNzMsMV0= \ No newline at end of file + - WzIwNzMsMV0= diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml index 5aaa73416bacd..a0ef24983502f 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/case_response_properties.yaml @@ -27,13 +27,13 @@ properties: $ref: 'assignees.yaml' category: type: - - "string" - - "null" + - 'string' + - 'null' description: The case category. closed_at: type: - - "string" - - "null" + - 'string' + - 'null' format: date-time closed_by: $ref: 'case_response_closed_by_properties.yaml' @@ -81,7 +81,6 @@ properties: customFields: type: array description: Custom field values for the case. - x-technical-preview: true items: type: object properties: @@ -92,8 +91,8 @@ properties: - A case description. duration: type: - - "integer" - - "null" + - 'integer' + - 'null' description: > The elapsed time from the creation of the case to its closure (in seconds). If the case has not been closed, the duration is set to null. If the case @@ -135,8 +134,8 @@ properties: - 0 updated_at: type: - - "string" - - "null" + - 'string' + - 'null' format: date-time updated_by: $ref: 'case_response_updated_by_properties.yaml' diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml index d58dea6472c21..94e9ecae9254d 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml @@ -40,7 +40,6 @@ properties: description: > Custom field values for a case. Any optional custom fields that are not specified in the request are set to null. - x-technical-preview: true minItems: 0 maxItems: 10 items: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/set_case_configuration_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/set_case_configuration_request.yaml index 6bcfad6a736c1..dd275406264b5 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/set_case_configuration_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/set_case_configuration_request.yaml @@ -21,7 +21,6 @@ properties: customFields: type: array description: Custom fields case configuration. - x-technical-preview: true minItems: 0 maxItems: 10 items: diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml index 56db4597f8b95..e359eea8e1030 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_configuration_request.yaml @@ -2,7 +2,7 @@ title: Update case configuration request description: > You can update settings such as the closure type, custom fields, templates, and the default connector for cases. type: object -required: +required: - version properties: closure_type: @@ -20,7 +20,6 @@ properties: customFields: type: array description: Custom fields case configuration. - x-technical-preview: true items: type: object required: @@ -38,4 +37,4 @@ properties: To retrieve the version value, use the get configuration API. type: string examples: - - WzIwMiwxXQ== \ No newline at end of file + - WzIwMiwxXQ== diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml index 3288f5a9476c0..8062128c03450 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml @@ -34,7 +34,6 @@ properties: description: > Custom field values for a case. Any optional custom fields that are not specified in the request are set to null. - x-technical-preview: true minItems: 0 maxItems: 10 items: diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index e83bbfd0013d1..2e11c3a64caae 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -153,6 +153,14 @@ export const ACTIONS = i18n.translate('xpack.cases.allCases.actions', { defaultMessage: 'Actions', }); +export const ACTIONS_BUTTON_ARIA_LABEL = (title: string) => + i18n.translate('xpack.cases.allCases.actions.button.ariaLabel', { + defaultMessage: 'Actions for "{title}" column', + values: { + title, + }, + }); + export const NO_TAGS_AVAILABLE = i18n.translate('xpack.cases.allCases.noTagsAvailable', { defaultMessage: 'No tags available', }); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx b/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx index 70a163bcd69a0..4c43201b1eab4 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_actions.tsx @@ -174,7 +174,7 @@ const ActionColumnComponent: React.FC<{ theCase: CaseUI; disableActions: boolean ; -describe('useGetSeverity', () => { +// FLAKY: https://github.com/elastic/kibana/issues/187456 +describe.skip('useGetSeverity', () => { const { http } = useKibanaMock().services; let appMockRender: AppMockRenderer; diff --git a/x-pack/plugins/cases/public/components/custom_fields/index.tsx b/x-pack/plugins/cases/public/components/custom_fields/index.tsx index a5068df682ad1..afe3a4ad04b32 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/index.tsx +++ b/x-pack/plugins/cases/public/components/custom_fields/index.tsx @@ -21,7 +21,6 @@ import { useCasesContext } from '../cases_context/use_cases_context'; import type { CustomFieldsConfiguration } from '../../../common/types/domain'; import { MAX_CUSTOM_FIELDS_PER_CASE } from '../../../common/constants'; import { CustomFieldsList } from './custom_fields_list'; -import { ExperimentalBadge } from '../experimental_badge/experimental_badge'; export interface Props { customFields: CustomFieldsConfiguration; @@ -68,14 +67,7 @@ const CustomFieldsComponent: React.FC = ({ return canAddCustomFields ? ( - {i18n.TITLE} - - - -
- } + title={

{i18n.TITLE}

} description={

{i18n.DESCRIPTION}

} data-test-subj="custom-fields-form-group" > diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx index b31f00c21c867..2b060778933d3 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useEffect, useRef } from 'react'; +import React, { Suspense, useEffect, useRef } from 'react'; import semverLt from 'semver/functions/lt'; import semverCoerce from 'semver/functions/coerce'; import semverValid from 'semver/functions/valid'; @@ -15,13 +15,13 @@ import { EuiForm, EuiFormRow, EuiHorizontalRule, + EuiLoadingSpinner, EuiSelect, EuiSpacer, EuiText, - EuiTextArea, EuiTitle, } from '@elastic/eui'; -import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; +import { LazyPackagePolicyInputVarField, type NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -30,6 +30,7 @@ import { GcpCredentialsType } from '../../../../common/types_old'; import { CLOUDBEAT_GCP } from '../../../../common/constants'; import { CspRadioOption, RadioGroup } from '../csp_boxed_radio_group'; import { + findVariableDef, getCspmCloudShellDefaultValue, getPosturePolicy, NewPackagePolicyPostureInput, @@ -193,7 +194,10 @@ const credentialOptionsList = [ }, ]; -type GcpFields = Record; +type GcpFields = Record< + string, + { label: string; type?: 'password' | 'text'; value?: string; isSecret?: boolean } +>; interface GcpInputFields { fields: GcpFields; } @@ -222,7 +226,8 @@ export const gcpField: GcpInputFields = { label: i18n.translate('xpack.csp.findings.gcpIntegration.gcpInputText.credentialJSONText', { defaultMessage: 'JSON blob containing the credentials and key used to subscribe', }), - type: 'text', + type: 'password', + isSecret: true, }, 'gcp.credentials.type': { label: i18n.translate( @@ -263,6 +268,7 @@ export interface GcpFormProps { setIsValid: (isValid: boolean) => void; onChange: any; disabled: boolean; + isEditPage?: boolean; } export const getInputVarsFields = (input: NewPackagePolicyInput, fields: GcpFields) => @@ -367,6 +373,7 @@ export const GcpCredentialsForm = ({ setIsValid, onChange, disabled, + isEditPage, }: GcpFormProps) => { /* Create a subset of properties from GcpField to use for hiding value of credentials json and credentials file when user switch from Manual to Cloud Shell, we wanna keep Project and Organization ID */ const subsetOfGcpField = (({ ['gcp.credentials.file']: a, ['gcp.credentials.json']: b }) => ({ @@ -489,6 +496,8 @@ export const GcpCredentialsForm = ({ updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } })) } isOrganization={isOrganization} + packageInfo={packageInfo} + isEditPage={isEditPage} /> )} @@ -504,11 +513,15 @@ export const GcpInputVarFields = ({ onChange, isOrganization, disabled, + packageInfo, + isEditPage, }: { fields: Array; onChange: (key: string, value: string) => void; isOrganization: boolean; disabled: boolean; + packageInfo: PackageInfo; + isEditPage?: boolean; }) => { const getFieldById = (id: keyof GcpInputFields['fields']) => { return fields.find((element) => element.id === id); @@ -581,15 +594,41 @@ export const GcpInputVarFields = ({ )} {credentialsTypeValue === credentialJSONValue && credentialJSONFields && ( - - onChange(credentialJSONFields.id, event.target.value)} - /> - +
+ + + }> + { + onChange(credentialJSONFields.id, value); + }} + errors={[]} + forceShowErrors={false} + isEditPage={isEditPage} + /> + + +
)} diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx index ca25f3ea1469e..d246ea82689bb 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credentials_form_agentless.tsx @@ -33,8 +33,8 @@ export const GcpCredentialsFormAgentless = ({ input, newPolicy, updatePolicy, - packageInfo, disabled, + packageInfo, }: GcpFormProps) => { const accountType = input.streams?.[0]?.vars?.['gcp.account_type']?.value; const isOrganization = accountType === ORGANIZATION_ACCOUNT; @@ -102,6 +102,7 @@ export const GcpCredentialsFormAgentless = ({ updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } })) } isOrganization={isOrganization} + packageInfo={packageInfo} /> diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index f3aafc11c9334..84abf584b5080 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -1226,48 +1226,6 @@ describe('', () => { }); }); - it(`renders ${CLOUDBEAT_GCP} Credentials JSON fields`, () => { - let policy = getMockPolicyGCP(); - policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - setup_access: { value: 'manual' }, - 'gcp.credentials.type': { value: 'credentials-json' }, - }); - - const { getByRole, getByLabelText } = render( - - ); - - expect(getByRole('option', { name: 'Credentials JSON', selected: true })).toBeInTheDocument(); - - expect( - getByLabelText('JSON blob containing the credentials and key used to subscribe') - ).toBeInTheDocument(); - }); - - it(`updates ${CLOUDBEAT_GCP} Credentials JSON fields`, () => { - let policy = getMockPolicyGCP(); - policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - 'gcp.project_id': { value: 'a' }, - 'gcp.credentials.type': { value: 'credentials-json' }, - setup_access: { value: 'manual' }, - }); - - const { getByTestId } = render( - - ); - - userEvent.type(getByTestId(CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS.CREDENTIALS_JSON), 'b'); - - policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { - 'gcp.credentials.json': { value: 'b' }, - }); - - expect(onChange).toHaveBeenCalledWith({ - isValid: true, - updatedPolicy: policy, - }); - }); - it(`${CLOUDBEAT_GCP} form do not displays upgrade message for supported versions and gcp organization option is enabled`, () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { @@ -1541,7 +1499,7 @@ describe('', () => { }); }); - it('should render setup technology selector for GCP for organisation account type', async () => { + it.skip('should render setup technology selector for GCP for organisation account type', async () => { const newPackagePolicy = getMockPolicyGCP(); const { getByTestId, queryByTestId, getByRole } = render( @@ -1593,7 +1551,7 @@ describe('', () => { }); }); - it('should render setup technology selector for GCP for single-account', async () => { + it.skip('should render setup technology selector for GCP for single-account', async () => { const newPackagePolicy = getMockPolicyGCP({ 'gcp.account_type': { value: GCP_SINGLE_ACCOUNT, type: 'text' }, }); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index a5096e5d29ef7..ab5ce05d7a54c 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -779,6 +779,7 @@ export const CspPolicyTemplateForm = memo diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx index fd816d03fb507..ee76d40e1dcac 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx @@ -79,6 +79,7 @@ interface PolicyTemplateVarsFormProps { setIsValid: (isValid: boolean) => void; disabled: boolean; setupTechnology: SetupTechnology; + isEditPage?: boolean; } export const PolicyTemplateVarsForm = ({ diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx index e2d453c078115..a2b1aa1d0d831 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx @@ -74,8 +74,11 @@ const CnvmIntegrationNotInstalledEmptyPrompt = ({

, + }} />

} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx index 5aa8aae1dc590..83745e5f5d113 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx @@ -333,8 +333,13 @@ const CurrentPageOfTotal = ({
diff --git a/x-pack/plugins/data_quality/common/index.ts b/x-pack/plugins/data_quality/common/index.ts index 79bbe59ff35d2..1b174a9efe524 100644 --- a/x-pack/plugins/data_quality/common/index.ts +++ b/x-pack/plugins/data_quality/common/index.ts @@ -13,6 +13,3 @@ export const PLUGIN_NAME = i18n.translate('xpack.dataQuality.name', { }); export { DATA_QUALITY_URL_STATE_KEY, datasetQualityUrlSchemaV1 } from './url_schema'; - -export { DATA_QUALITY_LOCATOR_ID } from './locators'; -export type { DataQualityLocatorParams } from './locators'; diff --git a/x-pack/plugins/data_quality/common/locators/construct_dataset_quality_locator_path.ts b/x-pack/plugins/data_quality/common/locators/construct_dataset_quality_locator_path.ts index 45f58752bd2fc..f5b5b0bf7ce59 100644 --- a/x-pack/plugins/data_quality/common/locators/construct_dataset_quality_locator_path.ts +++ b/x-pack/plugins/data_quality/common/locators/construct_dataset_quality_locator_path.ts @@ -8,9 +8,9 @@ import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/common'; import { ManagementAppLocatorParams } from '@kbn/management-plugin/common/locator'; import { LocatorPublic } from '@kbn/share-plugin/common'; +import { DataQualityLocatorParams } from '@kbn/deeplinks-observability'; import { datasetQualityUrlSchemaV1, DATA_QUALITY_URL_STATE_KEY } from '../url_schema'; import { deepCompactObject } from '../utils/deep_compact_object'; -import { DataQualityLocatorParams } from './types'; interface LocatorPathConstructionParams { locatorParams: DataQualityLocatorParams; @@ -20,7 +20,7 @@ interface LocatorPathConstructionParams { export const constructDatasetQualityLocatorPath = async (params: LocatorPathConstructionParams) => { const { - locatorParams: { filters }, + locatorParams: { filters, flyout }, useHash, managementLocator, } = params; @@ -29,6 +29,7 @@ export const constructDatasetQualityLocatorPath = async (params: LocatorPathCons deepCompactObject({ v: 1, filters, + flyout, }) ); diff --git a/x-pack/plugins/data_quality/common/locators/dataset_quality_locator.ts b/x-pack/plugins/data_quality/common/locators/dataset_quality_locator.ts index 70e4770090ef3..4bf804955b6bc 100644 --- a/x-pack/plugins/data_quality/common/locators/dataset_quality_locator.ts +++ b/x-pack/plugins/data_quality/common/locators/dataset_quality_locator.ts @@ -6,11 +6,8 @@ */ import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; -import { - DataQualityLocatorDependencies, - DataQualityLocatorParams, - DATA_QUALITY_LOCATOR_ID, -} from './types'; +import { DataQualityLocatorParams, DATA_QUALITY_LOCATOR_ID } from '@kbn/deeplinks-observability'; +import { DataQualityLocatorDependencies } from './types'; import { constructDatasetQualityLocatorPath } from './construct_dataset_quality_locator_path'; export type DatasetQualityLocator = LocatorPublic; diff --git a/x-pack/plugins/data_quality/common/locators/types.ts b/x-pack/plugins/data_quality/common/locators/types.ts index 57067cd0e482a..786b5e1cf567f 100644 --- a/x-pack/plugins/data_quality/common/locators/types.ts +++ b/x-pack/plugins/data_quality/common/locators/types.ts @@ -7,31 +7,6 @@ import { ManagementAppLocatorParams } from '@kbn/management-plugin/common/locator'; import { LocatorPublic } from '@kbn/share-plugin/common'; -import { SerializableRecord } from '@kbn/utility-types'; - -export const DATA_QUALITY_LOCATOR_ID = 'DATA_QUALITY_LOCATOR'; - -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -type RefreshInterval = { - pause: boolean; - value: number; -}; - -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -type TimeRangeConfig = { - from: string; - to: string; - refresh: RefreshInterval; -}; - -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -type Filters = { - timeRange: TimeRangeConfig; -}; - -export interface DataQualityLocatorParams extends SerializableRecord { - filters?: Filters; -} export interface DataQualityLocatorDependencies { useHash: boolean; diff --git a/x-pack/plugins/data_quality/common/url_schema/url_schema_v1.ts b/x-pack/plugins/data_quality/common/url_schema/url_schema_v1.ts index 6fd5781a217e8..076e1b641b7e2 100644 --- a/x-pack/plugins/data_quality/common/url_schema/url_schema_v1.ts +++ b/x-pack/plugins/data_quality/common/url_schema/url_schema_v1.ts @@ -37,11 +37,11 @@ const datasetRT = rt.intersection([ type: rt.string, name: rt.string, namespace: rt.string, - title: rt.string, }), rt.exact( rt.partial({ integration: integrationRT, + title: rt.string, }) ), ]); diff --git a/x-pack/plugins/data_quality/tsconfig.json b/x-pack/plugins/data_quality/tsconfig.json index 7a904e9f95cda..911c4fbfff557 100644 --- a/x-pack/plugins/data_quality/tsconfig.json +++ b/x-pack/plugins/data_quality/tsconfig.json @@ -25,8 +25,8 @@ "@kbn/core-chrome-browser", "@kbn/features-plugin", "@kbn/share-plugin", - "@kbn/utility-types", "@kbn/deeplinks-management", + "@kbn/deeplinks-observability", "@kbn/ebt-tools", ], "exclude": ["target/**/*"] diff --git a/x-pack/plugins/data_visualizer/kibana.jsonc b/x-pack/plugins/data_visualizer/kibana.jsonc index 1ad88eaea4cb4..84fc98d3fb22f 100644 --- a/x-pack/plugins/data_visualizer/kibana.jsonc +++ b/x-pack/plugins/data_visualizer/kibana.jsonc @@ -37,7 +37,7 @@ "fieldFormats", "uiActions", "lens", - "textBasedLanguages", + "esql", "visualizations" ] } diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx index 4ffe604c3d352..fd65ed3c7dfa6 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx @@ -12,7 +12,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { usePageUrlState } from '@kbn/ml-url-state'; import { FullTimeRangeSelector, DatePickerWrapper } from '@kbn/ml-date-picker'; -import { TextBasedLangEditor } from '@kbn/text-based-languages/public'; +import { TextBasedLangEditor } from '@kbn/esql/public'; import type { AggregateQuery } from '@kbn/es-query'; import { diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx index bdaee8c1a5ae1..a015d975fdf18 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { useRef, useState, useCallback } from 'react'; -import { TextBasedLangEditor } from '@kbn/text-based-languages/public'; +import { TextBasedLangEditor } from '@kbn/esql/public'; import { EuiFlexItem } from '@elastic/eui'; import type { AggregateQuery } from '@kbn/es-query'; diff --git a/x-pack/plugins/data_visualizer/tsconfig.json b/x-pack/plugins/data_visualizer/tsconfig.json index 9616783094354..ed8a3540f6d8a 100644 --- a/x-pack/plugins/data_visualizer/tsconfig.json +++ b/x-pack/plugins/data_visualizer/tsconfig.json @@ -68,7 +68,7 @@ "@kbn/security-plugin", "@kbn/share-plugin", "@kbn/test-jest-helpers", - "@kbn/text-based-languages", + "@kbn/esql", "@kbn/ui-actions-plugin", "@kbn/ui-theme", "@kbn/unified-search-plugin", diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 47c4741e41afc..f2be720d1c04c 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -8,7 +8,7 @@ import { ENTERPRISE_SEARCH_APP_ID, ENTERPRISE_SEARCH_CONTENT_APP_ID, - ENTERPRISE_SEARCH_INFERENCE_ENDPOINTS_APP_ID, + ENTERPRISE_SEARCH_RELEVANCE_APP_ID, ENTERPRISE_SEARCH_APPLICATIONS_APP_ID, ENTERPRISE_SEARCH_ANALYTICS_APP_ID, ENTERPRISE_SEARCH_APPSEARCH_APP_ID, @@ -178,7 +178,7 @@ export const VECTOR_SEARCH_PLUGIN = { }; export const INFERENCE_ENDPOINTS_PLUGIN = { - ID: ENTERPRISE_SEARCH_INFERENCE_ENDPOINTS_APP_ID, + ID: ENTERPRISE_SEARCH_RELEVANCE_APP_ID, NAME: i18n.translate('xpack.enterpriseSearch.inferenceEndpoints.productName', { defaultMessage: 'Inference Endpoints', }), diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/licensing_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/licensing_logic.mock.ts index 0ea858d0f2f50..4f91ddf4cb5c6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/licensing_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/licensing_logic.mock.ts @@ -10,6 +10,7 @@ import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; export const mockLicensingValues = { license: licensingMock.createLicense(), hasPlatinumLicense: false, + hasEnterpriseLicense: true, hasGoldLicense: false, isTrial: false, canManageLicense: true, diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx index e6882acb0ac3b..b117518d3a6e0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx @@ -9,11 +9,8 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; - import { KibanaLogic } from '../../../shared/kibana'; import { EnterpriseSearchApplicationsPageTemplate } from '../layout/page_template'; @@ -31,32 +28,9 @@ export const Playground: React.FC = () => { defaultMessage: 'Playground', }), ]} - pageHeader={{ - pageTitle: ( - - - - - - - - - ), - rightSideItems: [], - }} pageViewTelemetry="Playground" restrictWidth={false} + panelled={false} customPageSections bottomBorder="extended" docLink="playground" diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx index 77454581c61e7..e39f0f0b71f29 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx @@ -40,6 +40,8 @@ import { import { INFERENCE_ENDPOINTS_PATH } from '../../enterprise_search_relevance/routes'; import { KibanaLogic } from '../kibana'; +import { LicensingLogic } from '../licensing'; + import { generateNavLink } from './nav_link_helpers'; /** @@ -51,7 +53,11 @@ import { generateNavLink } from './nav_link_helpers'; export const useEnterpriseSearchNav = (alwaysReturn = false) => { const { isSearchHomepageEnabled, searchHomepage, isSidebarEnabled, productAccess } = useValues(KibanaLogic); + + const { hasEnterpriseLicense } = useValues(LicensingLogic); + const indicesNavItems = useIndicesNav(); + if (!isSidebarEnabled && !alwaysReturn) return undefined; const navItems: Array> = [ @@ -154,25 +160,29 @@ export const useEnterpriseSearchNav = (alwaysReturn = false) => { defaultMessage: 'Build', }), }, - { - id: 'relevance', - items: [ - { - id: 'inference_endpoints', - name: i18n.translate('xpack.enterpriseSearch.nav.inferenceEndpointsTitle', { - defaultMessage: 'Inference Endpoints', - }), - ...generateNavLink({ - shouldNotCreateHref: true, - shouldShowActiveForSubroutes: true, - to: INFERENCE_ENDPOINTS_PLUGIN.URL + INFERENCE_ENDPOINTS_PATH, - }), - }, - ], - name: i18n.translate('xpack.enterpriseSearch.nav.relevanceTitle', { - defaultMessage: 'Relevance', - }), - }, + ...(hasEnterpriseLicense + ? [ + { + id: 'relevance', + items: [ + { + id: 'inference_endpoints', + name: i18n.translate('xpack.enterpriseSearch.nav.inferenceEndpointsTitle', { + defaultMessage: 'Inference Endpoints', + }), + ...generateNavLink({ + shouldNotCreateHref: true, + shouldShowActiveForSubroutes: true, + to: INFERENCE_ENDPOINTS_PLUGIN.URL + INFERENCE_ENDPOINTS_PATH, + }), + }, + ], + name: i18n.translate('xpack.enterpriseSearch.nav.relevanceTitle', { + defaultMessage: 'Relevance', + }), + }, + ] + : []), { id: 'es_getting_started', items: [ diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts index 85a26abeef1e1..743483e96fa31 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts @@ -167,6 +167,38 @@ describe('LicensingLogic', () => { }); }); + describe('hasEnterpriseLicense', () => { + it('is true for enterprise and trial licenses', () => { + updateLicense({ status: 'active', type: 'enterprise' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'trial' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(true); + }); + + it('is false if the current license is expired', () => { + updateLicense({ status: 'expired', type: 'enterprise' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'trial' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(false); + }); + + it('is false for licenses below enterprise', () => { + updateLicense({ status: 'active', type: 'gold' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'platinum' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'basic' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'standard' }); + expect(LicensingLogic.values.hasEnterpriseLicense).toEqual(false); + }); + }); + describe('isTrial', () => { it('is true for active trial license', () => { updateLicense({ status: 'active', type: 'trial' }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts index 77a09de2c863f..ab3586f6563c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts @@ -13,6 +13,7 @@ import { ILicense } from '@kbn/licensing-plugin/public'; interface LicensingValues { license: ILicense | null; licenseSubscription: Subscription | null; + hasEnterpriseLicense: boolean; hasPlatinumLicense: boolean; hasGoldLicense: boolean; isTrial: boolean; @@ -52,6 +53,13 @@ export const LicensingLogic = kea [selectors.license], + (license) => { + const qualifyingLicenses = ['enterprise', 'trial']; + return license?.isActive && qualifyingLicenses.includes(license?.type); + }, + ], hasGoldLicense: [ (selectors) => [selectors.license], (license) => { diff --git a/x-pack/plugins/enterprise_search/public/navigation_tree.ts b/x-pack/plugins/enterprise_search/public/navigation_tree.ts index 8bb6bf70e603b..d5c640fa67b3e 100644 --- a/x-pack/plugins/enterprise_search/public/navigation_tree.ts +++ b/x-pack/plugins/enterprise_search/public/navigation_tree.ts @@ -211,7 +211,7 @@ export const getNavigationTreeDefinition = ({ }), }, { - children: [{ link: 'searchInferenceEndpoints' }], + children: [{ link: 'enterpriseSearchRelevance:inferenceEndpoints' }], id: 'relevance', title: i18n.translate('xpack.enterpriseSearch.searchNav.relevance', { defaultMessage: 'Relevance', diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index 280de2f04356b..552bb43fbd073 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { BehaviorSubject, firstValueFrom } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, Subscription } from 'rxjs'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public'; @@ -27,6 +27,7 @@ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { i18n } from '@kbn/i18n'; import type { IndexManagementPluginStart } from '@kbn/index-management'; import { LensPublicStart } from '@kbn/lens-plugin/public'; +import { ILicense } from '@kbn/licensing-plugin/public'; import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { MlPluginStart } from '@kbn/ml-plugin/public'; import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; @@ -84,6 +85,7 @@ export type EnterpriseSearchPublicStart = ReturnType(); @@ -261,7 +264,8 @@ export class EnterpriseSearchPlugin implements Plugin { if (!config.ui?.enabled) { return; } - const { cloud, share } = plugins; + const { cloud, share, licensing } = plugins; + const useSearchHomepage = plugins.searchHomepage && plugins.searchHomepage.isHomepageFeatureEnabled(); @@ -445,29 +449,33 @@ export class EnterpriseSearchPlugin implements Plugin { title: ANALYTICS_PLUGIN.NAME, }); - core.application.register({ - appRoute: INFERENCE_ENDPOINTS_PLUGIN.URL, - category: DEFAULT_APP_CATEGORIES.enterpriseSearch, - deepLinks: relevanceLinks, - euiIconType: INFERENCE_ENDPOINTS_PLUGIN.LOGO, - id: INFERENCE_ENDPOINTS_PLUGIN.ID, - mount: async (params: AppMountParameters) => { - const kibanaDeps = await this.getKibanaDeps(core, params, cloud); - const { chrome, http } = kibanaDeps.core; - chrome.docTitle.change(INFERENCE_ENDPOINTS_PLUGIN.NAME); - - await this.getInitialData(http); - const pluginData = this.getPluginData(); - - const { renderApp } = await import('./applications'); - const { EnterpriseSearchRelevance } = await import( - './applications/enterprise_search_relevance' - ); - - return renderApp(EnterpriseSearchRelevance, kibanaDeps, pluginData); - }, - title: INFERENCE_ENDPOINTS_PLUGIN.NAME, - visibleIn: [], + this.licenseSubscription = licensing?.license$.subscribe((license: ILicense) => { + if (license.isActive && license.hasAtLeast('enterprise')) { + core.application.register({ + appRoute: INFERENCE_ENDPOINTS_PLUGIN.URL, + category: DEFAULT_APP_CATEGORIES.enterpriseSearch, + deepLinks: relevanceLinks, + euiIconType: INFERENCE_ENDPOINTS_PLUGIN.LOGO, + id: INFERENCE_ENDPOINTS_PLUGIN.ID, + mount: async (params: AppMountParameters) => { + const kibanaDeps = await this.getKibanaDeps(core, params, cloud); + const { chrome, http } = kibanaDeps.core; + chrome.docTitle.change(INFERENCE_ENDPOINTS_PLUGIN.NAME); + + await this.getInitialData(http); + const pluginData = this.getPluginData(); + + const { renderApp } = await import('./applications'); + const { EnterpriseSearchRelevance } = await import( + './applications/enterprise_search_relevance' + ); + + return renderApp(EnterpriseSearchRelevance, kibanaDeps, pluginData); + }, + title: INFERENCE_ENDPOINTS_PLUGIN.NAME, + visibleIn: [], + }); + } }); core.application.register({ @@ -645,7 +653,9 @@ export class EnterpriseSearchPlugin implements Plugin { return {}; } - public stop() {} + public stop() { + this.licenseSubscription?.unsubscribe(); + } private updateSideNavDefinition = (items: Partial) => { this.sideNavDynamicItems$.next({ ...this.sideNavDynamicItems$.getValue(), ...items }); diff --git a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.test.ts b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.test.ts index 9e017424a8f3d..9f1325f2c7a3d 100644 --- a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.test.ts @@ -9,15 +9,16 @@ import { mockLogger } from '../../__mocks__'; import { MlTrainedModels } from '@kbn/ml-plugin/server'; -import { MlModelDeploymentState } from '../../../common/types/ml'; - -import { fetchMlModels } from './fetch_ml_models'; import { E5_LINUX_OPTIMIZED_MODEL_ID, E5_MODEL_ID, ELSER_LINUX_OPTIMIZED_MODEL_ID, ELSER_MODEL_ID, -} from './utils'; +} from '@kbn/ml-trained-models-utils'; + +import { MlModelDeploymentState } from '../../../common/types/ml'; + +import { fetchMlModels } from './fetch_ml_models'; describe('fetchMlModels', () => { const mockTrainedModelsProvider = { diff --git a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts index 595c35b33755e..889b7bb1b89e1 100644 --- a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts +++ b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts @@ -11,6 +11,14 @@ import { Logger } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; import { MlTrainedModels } from '@kbn/ml-plugin/server'; +import { + E5_LINUX_OPTIMIZED_MODEL_ID, + E5_MODEL_ID, + ELSER_LINUX_OPTIMIZED_MODEL_ID, + ELSER_MODEL_ID, + LANG_IDENT_MODEL_ID, +} from '@kbn/ml-trained-models-utils'; + import { getMlModelTypesForModelConfig } from '../../../common/ml_inference_pipeline'; import { MlModelDeploymentState, MlModel } from '../../../common/types/ml'; @@ -18,15 +26,10 @@ import { MlModelDeploymentState, MlModel } from '../../../common/types/ml'; import { BASE_MODEL, ELSER_LINUX_OPTIMIZED_MODEL_PLACEHOLDER, - ELSER_MODEL_ID, ELSER_MODEL_PLACEHOLDER, E5_LINUX_OPTIMIZED_MODEL_PLACEHOLDER, - E5_MODEL_ID, E5_MODEL_PLACEHOLDER, - LANG_IDENT_MODEL_ID, MODEL_TITLES_BY_TYPE, - E5_LINUX_OPTIMIZED_MODEL_ID, - ELSER_LINUX_OPTIMIZED_MODEL_ID, } from './utils'; let compatibleElserModelId = ELSER_MODEL_ID; diff --git a/x-pack/plugins/enterprise_search/server/lib/ml/utils.ts b/x-pack/plugins/enterprise_search/server/lib/ml/utils.ts index d063fd158385a..20c9fe7ce0193 100644 --- a/x-pack/plugins/enterprise_search/server/lib/ml/utils.ts +++ b/x-pack/plugins/enterprise_search/server/lib/ml/utils.ts @@ -8,13 +8,14 @@ import { i18n } from '@kbn/i18n'; import { SUPPORTED_PYTORCH_TASKS } from '@kbn/ml-trained-models-utils'; -import { MlModelDeploymentState, MlModel } from '../../../common/types/ml'; +import { + E5_LINUX_OPTIMIZED_MODEL_ID, + E5_MODEL_ID, + ELSER_LINUX_OPTIMIZED_MODEL_ID, + ELSER_MODEL_ID, +} from '@kbn/ml-trained-models-utils'; -export const ELSER_MODEL_ID = '.elser_model_2'; -export const ELSER_LINUX_OPTIMIZED_MODEL_ID = '.elser_model_2_linux-x86_64'; -export const E5_MODEL_ID = '.multilingual-e5-small'; -export const E5_LINUX_OPTIMIZED_MODEL_ID = '.multilingual-e5-small_linux-x86_64'; -export const LANG_IDENT_MODEL_ID = 'lang_ident_model_1'; +import { MlModelDeploymentState, MlModel } from '../../../common/types/ml'; export const MODEL_TITLES_BY_TYPE: Record = { fill_mask: i18n.translate('xpack.enterpriseSearch.content.ml_inference.fill_mask', { diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 24298c55fdffa..ee403e223305f 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -21,6 +21,7 @@ import { DataPluginStart } from '@kbn/data-plugin/server/plugin'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; import { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/server'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; +import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { LogsSharedPluginSetup } from '@kbn/logs-shared-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import { SearchConnectorsPluginSetup } from '@kbn/search-connectors-plugin/server'; @@ -95,6 +96,7 @@ interface PluginsSetup { guidedOnboarding?: GuidedOnboardingPluginSetup; logsShared: LogsSharedPluginSetup; ml?: MlPluginSetup; + licensing: LicensingPluginStart; searchConnectors?: SearchConnectorsPluginSetup; security: SecurityPluginSetup; usageCollection?: UsageCollectionSetup; @@ -148,6 +150,7 @@ export class EnterpriseSearchPlugin implements Plugin { logsShared, customIntegrations, ml, + licensing, guidedOnboarding, cloud, searchConnectors, @@ -262,6 +265,7 @@ export class EnterpriseSearchPlugin implements Plugin { log, enterpriseSearchRequestHandler, ml, + licensing, }; registerConfigDataRoute(dependencies); diff --git a/x-pack/plugins/fleet/common/constants/locators.ts b/x-pack/plugins/fleet/common/constants/locators.ts index daa00bcae46e0..cca0687a172d8 100644 --- a/x-pack/plugins/fleet/common/constants/locators.ts +++ b/x-pack/plugins/fleet/common/constants/locators.ts @@ -8,6 +8,7 @@ export const LOCATORS_IDS = { APM_LOCATOR: 'APM_LOCATOR', DASHBOARD_APP: 'DASHBOARD_APP_LOCATOR', + DISCOVER_APP_LOCATOR: 'DISCOVER_APP_LOCATOR', } as const; // Dashboards ids diff --git a/x-pack/plugins/fleet/common/experimental_features.ts b/x-pack/plugins/fleet/common/experimental_features.ts index 1e6af81b8c445..502d3b603f159 100644 --- a/x-pack/plugins/fleet/common/experimental_features.ts +++ b/x-pack/plugins/fleet/common/experimental_features.ts @@ -27,7 +27,7 @@ const _allowedExperimentalValues = { enablePackagesStateMachine: true, advancedPolicySettings: true, useSpaceAwareness: false, - enableReusableIntegrationPolicies: false, + enableReusableIntegrationPolicies: true, }; /** diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 06a8b979c8eb5..fda07095de95d 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -435,6 +435,7 @@ export enum RegistryVarsEntryKeys { os = 'os', secret = 'secret', hide_in_deployment_modes = 'hide_in_deployment_modes', + full_width = 'full_width', } // EPR types this as `[]map[string]interface{}` @@ -457,6 +458,7 @@ export interface RegistryVarsEntry { }; }; [RegistryVarsEntryKeys.hide_in_deployment_modes]?: string[]; + [RegistryVarsEntryKeys.full_width]?: boolean; } // Deprecated as part of the removing public references to saved object schemas diff --git a/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts b/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts index 0478c450ae5a2..269c848f998bf 100644 --- a/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts @@ -82,8 +82,13 @@ describe('Input package create and edit package policy', () => { cy.getBySel(EXISTING_HOSTS_TAB).click(); - cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).click().get(`#${agentPolicyId}`).click(); - cy.wait(500); // wait for policy id to be set + cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).click(); + cy.getBySel('agentPolicyMultiItem').each(($el) => { + if ($el.text() === agentPolicyName) { + $el.trigger('click'); + } + }); + cy.wait(1000); // wait for policy id to be set cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click(); cy.getBySel(CONFIRM_MODAL.CANCEL_BUTTON).click(); @@ -150,8 +155,13 @@ describe('Integration package with custom dataset create and edit package policy cy.getBySel(EXISTING_HOSTS_TAB).click(); - cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).click().get(`#${agentPolicyId}`).click(); - cy.wait(500); // wait for policy id to be set + cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).click(); + cy.getBySel('agentPolicyMultiItem').each(($el) => { + if ($el.text() === agentPolicyName) { + $el.trigger('click'); + } + }); + cy.wait(1000); // wait for policy id to be set cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click(); cy.getBySel(CONFIRM_MODAL.CANCEL_BUTTON).click(); @@ -210,8 +220,13 @@ describe('Integration package with fixed dataset create and edit package policy' cy.getBySel(EXISTING_HOSTS_TAB).click(); - cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).click().get(`#${agentPolicyId}`).click(); - cy.wait(500); // wait for policy id to be set + cy.getBySel(POLICY_EDITOR.AGENT_POLICY_SELECT).click(); + cy.getBySel('agentPolicyMultiItem').each(($el) => { + if ($el.text() === agentPolicyName) { + $el.trigger('click'); + } + }); + cy.wait(1000); // wait for policy id to be set cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click(); cy.getBySel(CONFIRM_MODAL.CANCEL_BUTTON).click(); diff --git a/x-pack/plugins/fleet/cypress/screens/integrations.ts b/x-pack/plugins/fleet/cypress/screens/integrations.ts index 7cc6aefdf89ec..1a31d2dc5de31 100644 --- a/x-pack/plugins/fleet/cypress/screens/integrations.ts +++ b/x-pack/plugins/fleet/cypress/screens/integrations.ts @@ -38,7 +38,7 @@ export const SETTINGS = { export const POLICY_EDITOR = { POLICY_NAME_INPUT: 'packagePolicyNameInput', DATASET_SELECT: 'datasetComboBox', - AGENT_POLICY_SELECT: 'agentPolicySelect', + AGENT_POLICY_SELECT: 'agentPolicyMultiSelect', INSPECT_PIPELINES_BTN: 'datastreamInspectPipelineBtn', EDIT_MAPPINGS_BTN: 'datastreamEditMappingsBtn', CREATE_MAPPINGS_BTN: 'datastreamAddCustomComponentTemplateBtn', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx index 114d973f32541..af0b2dbce2ff1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx @@ -185,7 +185,7 @@ function getInputComponent({ fieldTestSelector, setIsDirty, }: InputComponentProps) { - const { multi, type, options } = varDef; + const { multi, type, options, full_width: fullWidth } = varDef; if (multi) { return ( setIsDirty(true)} disabled={frozen} resize="vertical" + fullWidth={fullWidth} data-test-subj={`textAreaInput-${fieldTestSelector}`} /> ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.test.tsx index 30688c7a99b11..237ab76465fdd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.test.tsx @@ -105,7 +105,7 @@ describe('step select agent policy', () => { const select = renderResult.container.querySelector('[data-test-subj="agentPolicySelect"]'); expect((select as any)?.value).toEqual(''); - expect(renderResult.getByText('An agent policy is required.')).toBeVisible(); + expect(renderResult.getByText('At least one agent policy is required.')).toBeVisible(); }); }); @@ -178,7 +178,7 @@ describe('step select agent policy', () => { ); expect((select as any)?.value).toEqual(undefined); - expect(renderResult.getByText('An agent policy is required.')).toBeVisible(); + expect(renderResult.getByText('At least one agent policy is required.')).toBeVisible(); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.tsx index 6238a2cc62a07..ecfd18ef9aac4 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/step_select_agent_policy.tsx @@ -101,6 +101,9 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ updateAgentPolicies([]); } }; + if (isLoading || selectedPolicyIds.length === 0) { + return; + } const agentPoliciesHaveAllSelectedIds = selectedPolicyIds.every((id) => agentPolicies.map((policy) => policy.id).includes(id) ); @@ -110,7 +113,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ setSelectedAgentPolicyError(undefined); updateAgentPolicies(agentPolicies.filter((policy) => selectedPolicyIds.includes(policy.id))); } - }, [selectedPolicyIds, agentPolicies, updateAgentPolicies]); + }, [selectedPolicyIds, agentPolicies, updateAgentPolicies, isLoading]); // Try to select default agent policy useEffect(() => { @@ -192,7 +195,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{

@@ -215,7 +218,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
@@ -241,7 +244,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ selectedPolicyIds.length === 0 ? ( ) : someNewAgentPoliciesHaveLimitedPackage ? ( { }); waitFor(() => { - expect(renderResult.getByText('An agent policy is required.')).toBeInTheDocument(); + expect(renderResult.getByText('At least one agent policy is required.')).toBeInTheDocument(); }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index 0054fc71133a2..4109a66a59638 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -378,7 +378,6 @@ describe('When on the package policy create page', () => { expect(sendCreatePackagePolicy as jest.MockedFunction).toHaveBeenCalledWith({ ...newPackagePolicy, - policy_id: 'agent-policy-1', policy_ids: ['agent-policy-1'], force: false, }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx index e9bbde2520837..2cecdf37f30f5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/package_policies_table.tsx @@ -113,7 +113,7 @@ export const PackagePoliciesTable: React.FunctionComponent = ({ sortable: true, truncateText: true, name: i18n.translate('xpack.fleet.policyDetails.packagePoliciesTable.nameColumnTitle', { - defaultMessage: 'Name', + defaultMessage: 'Integration policy', }), render: (value: string, packagePolicy: InMemoryPackagePolicy) => ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index acaf623afa330..98096d02138f9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -144,6 +144,13 @@ export const EditPackagePolicyForm = memo<{ const [isFirstLoad, setIsFirstLoad] = useState(true); const [newAgentPolicyName, setNewAgentPolicyName] = useState(); + // make form dirty if new agent policy is selected + useEffect(() => { + if (newAgentPolicyName) { + setIsEdited(true); + } + }, [newAgentPolicyName, setIsEdited]); + const [hasAgentPolicyError, setHasAgentPolicyError] = useState(false); // Retrieve agent count diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx index 7824b8abd2a5c..78e32727212c9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx @@ -12,8 +12,6 @@ import { createFleetTestRendererMock } from '../../../../../../../mock'; import { AgentLogsUI } from './agent_logs'; -jest.mock('../../../../../../../hooks/use_authz'); - jest.mock('@kbn/kibana-utils-plugin/public', () => { return { ...jest.requireActual('@kbn/kibana-utils-plugin/public'), @@ -28,6 +26,13 @@ jest.mock('@kbn/logs-shared-plugin/public', () => { LogStream: () =>
, }; }); +jest.mock('@kbn/logs-shared-plugin/common', () => { + return { + getLogsLocatorsFromUrlService: jest.fn().mockReturnValue({ + logsLocator: { getRedirectUrl: jest.fn(() => 'https://discover-redirect-url') }, + }), + }; +}); jest.mock('@kbn/shared-ux-link-redirect-app', () => { return { @@ -52,6 +57,13 @@ jest.mock('../../../../../hooks', () => { ...jest.requireActual('../../../../../hooks'), useLink: jest.fn(), useStartServices: jest.fn(), + useAuthz: jest.fn(), + useDiscoverLocator: jest.fn().mockImplementation(() => { + return { + id: 'DISCOVER_APP_LOCATOR', + getRedirectUrl: jest.fn().mockResolvedValue('app/discover/logs/someview'), + }; + }), }; }); @@ -62,6 +74,7 @@ describe('AgentLogsUI', () => { jest.mocked(useAuthz).mockReturnValue({ fleet: { allAgents: true, + readAgents: true, }, } as any); }); @@ -100,34 +113,36 @@ describe('AgentLogsUI', () => { }, }, }, - http: { - basePath: { - prepend: (url: string) => 'http://localhost:5620' + url, + share: { + url: { + locators: { + get: () => ({ + useUrl: () => 'https://locator.url', + }), + }, }, }, - cloud: { - isServerlessEnabled, - }, }); }; - it('should render Open in Logs UI if capabilities not set', () => { + it('should render Open in Logs button if privileges are set', () => { mockStartServices(); const result = renderComponent(); expect(result.getByTestId('viewInLogsBtn')).toHaveAttribute( 'href', - `http://localhost:5620/app/logs/stream?logPosition=(end%3A'2023-20-04T14%3A20%3A00.340Z'%2Cstart%3A'2023-20-04T14%3A00%3A00.340Z'%2CstreamLive%3A!f)&logFilter=(expression%3A'elastic_agent.id%3Aagent1%20and%20(data_stream.dataset%3Aelastic_agent)%20and%20(log.level%3Ainfo%20or%20log.level%3Aerror)'%2Ckind%3Akuery)` + `https://discover-redirect-url` ); }); - it('should render Open in Discover if serverless enabled', () => { - mockStartServices(true); + it('should not render Open in Logs button if privileges are not set', () => { + jest.mocked(useAuthz).mockReturnValue({ + fleet: { + readAgents: false, + }, + } as any); + mockStartServices(); const result = renderComponent(); - const viewInDiscover = result.getByTestId('viewInDiscoverBtn'); - expect(viewInDiscover).toHaveAttribute( - 'href', - `http://localhost:5620/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:'2023-20-04T14:00:00.340Z',to:'2023-20-04T14:20:00.340Z'))&_a=(columns:!(event.dataset,message),index:'logs-*',query:(language:kuery,query:'elastic_agent.id:agent1 and (data_stream.dataset:elastic_agent) and (log.level:info or log.level:error)'))` - ); + expect(result.queryByTestId('viewInLogsBtn')).not.toBeInTheDocument(); }); it('should show log level dropdown with correct value', () => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx index 34cc206967d62..5a9bfecd22144 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx @@ -35,7 +35,7 @@ import { LogLevelFilter } from './filter_log_level'; import { LogQueryBar } from './query_bar'; import { buildQuery } from './build_query'; import { SelectLogLevel } from './select_log_level'; -import { ViewLogsButton } from './view_logs_button'; +import { ViewLogsButton, getFormattedRange } from './view_logs_button'; const WrapperFlexGroup = styled(EuiFlexGroup)` height: 100%; @@ -112,9 +112,8 @@ const AgentPolicyLogsNotEnabledCallout: React.FunctionComponent<{ agentPolicy: A export const AgentLogsUI: React.FunctionComponent = memo( ({ agent, agentPolicy, state }) => { - const { data, application, cloud } = useStartServices(); + const { data, application } = useStartServices(); const { update: updateState } = AgentLogsUrlStateHelper.useTransitions(); - const isLogsUIAvailable = !cloud?.isServerlessEnabled; // Util to convert date expressions (returned by datepicker) to timestamps (used by LogStream) const getDateRangeTimestamps = useCallback( @@ -321,10 +320,9 @@ export const AgentLogsUI: React.FunctionComponent = memo( }} > diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/view_logs_button.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/view_logs_button.tsx index 762c34ad7bc36..7b859596987c0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/view_logs_button.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/view_logs_button.tsx @@ -5,81 +5,61 @@ * 2.0. */ -import url from 'url'; -import { stringify } from 'querystring'; - import React, { useMemo } from 'react'; -import { encode } from '@kbn/rison'; import { EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useStartServices } from '../../../../../hooks'; +import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common'; + +import moment from 'moment'; + +import { useDiscoverLocator, useStartServices, useAuthz } from '../../../../../hooks'; interface ViewLogsProps { - viewInLogs: boolean; logStreamQuery: string; - startTime: string; - endTime: string; + startTime: number; + endTime: number; } +export const getFormattedRange = (date: string) => new Date(date).getTime(); + /* - Button that takes to the Logs view Ui when that is available, otherwise fallback to the Discover UI - The urls are built using same logStreamQuery (provided by a prop), startTime and endTime, ensuring that they'll both will target same log lines + Button that takes to the Logs view UI or the Discover logs, depending on what's available + If none is available, don't display the button at all */ export const ViewLogsButton: React.FunctionComponent = ({ - viewInLogs, logStreamQuery, startTime, endTime, }) => { - const { http } = useStartServices(); + const discoverLocator = useDiscoverLocator(); - // Generate URL to pass page state to Logs UI - const viewInLogsUrl = useMemo( - () => - http.basePath.prepend( - url.format({ - pathname: '/app/logs/stream', - search: stringify({ - logPosition: encode({ - start: startTime, - end: endTime, - streamLive: false, - }), - logFilter: encode({ - expression: logStreamQuery, - kind: 'kuery', - }), - }), - }) - ), - [http.basePath, startTime, endTime, logStreamQuery] - ); + const { share } = useStartServices(); + const { logsLocator } = getLogsLocatorsFromUrlService(share.url); + const authz = useAuthz(); - const viewInDiscoverUrl = useMemo(() => { - const index = 'logs-*'; - const query = encode({ - query: logStreamQuery, - language: 'kuery', + const logsUrl = useMemo(() => { + const now = moment().toISOString(); + const oneDayAgo = moment().subtract(1, 'day').toISOString(); + const defaultStartTime = getFormattedRange(oneDayAgo); + const defaultEndTime = getFormattedRange(now); + + return logsLocator.getRedirectUrl({ + time: endTime ? endTime : defaultEndTime, + timeRange: { + startTime: startTime ? startTime : defaultStartTime, + endTime: endTime ? endTime : defaultEndTime, + }, + filter: logStreamQuery, }); - return http.basePath.prepend( - `/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:'${startTime}',to:'${endTime}'))&_a=(columns:!(event.dataset,message),index:'${index}',query:${query})` - ); - }, [logStreamQuery, http.basePath, startTime, endTime]); + }, [endTime, logStreamQuery, logsLocator, startTime]); - return viewInLogs ? ( - + return authz.fleet.readAgents && (logsLocator || discoverLocator) ? ( + - ) : ( - - - - ); + ) : null; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout/index.test.tsx index 433cd687208fc..c649b3829a41e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_activity_flyout/index.test.tsx @@ -11,7 +11,7 @@ import { act, render, fireEvent } from '@testing-library/react'; import { IntlProvider } from 'react-intl'; import { useActionStatus } from '../../hooks'; -import { useGetAgentPolicies, useStartServices } from '../../../../../hooks'; +import { useGetAgentPolicies, useStartServices, useAuthz } from '../../../../../hooks'; import { AgentActivityFlyout } from '.'; @@ -25,6 +25,15 @@ jest.mock('@kbn/shared-ux-link-redirect-app', () => ({ const mockUseActionStatus = useActionStatus as jest.Mock; const mockUseGetAgentPolicies = useGetAgentPolicies as jest.Mock; const mockUseStartServices = useStartServices as jest.Mock; +const mockedUseAuthz = useAuthz as jest.Mock; + +jest.mock('@kbn/logs-shared-plugin/common', () => { + return { + getLogsLocatorsFromUrlService: jest.fn().mockReturnValue({ + logsLocator: { getRedirectUrl: jest.fn(() => 'https://discover-redirect-url') }, + }), + }; +}); describe('AgentActivityFlyout', () => { const mockOnClose = jest.fn(); @@ -65,7 +74,22 @@ describe('AgentActivityFlyout', () => { docLinks: { links: { fleet: { upgradeElasticAgent: 'https://elastic.co' } } }, application: { navigateToUrl: jest.fn() }, http: { basePath: { prepend: jest.fn() } }, + share: { + url: { + locators: { + get: () => ({ + useUrl: () => 'https://locator.url', + }), + }, + }, + }, }); + mockedUseAuthz.mockReturnValue({ + fleet: { + readAgents: true, + allAgents: true, + }, + } as any); }); beforeEach(() => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.test.tsx index b5018f812da4e..e8f73eae3a2b9 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.test.tsx @@ -12,7 +12,7 @@ import { I18nProvider } from '@kbn/i18n-react'; import type { ActionStatus } from '../../../../../../../common/types'; -import { useStartServices } from '../../../../hooks'; +import { useStartServices, useAuthz } from '../../../../hooks'; import { ViewErrors } from './view_errors'; @@ -21,6 +21,13 @@ jest.mock('../../../../hooks', () => { ...jest.requireActual('../../../../hooks'), useLink: jest.fn(), useStartServices: jest.fn(), + useAuthz: jest.fn(), + useDiscoverLocator: jest.fn().mockImplementation(() => { + return { + id: 'DISCOVER_APP_LOCATOR', + getRedirectUrl: jest.fn().mockResolvedValue('app/discover/logs/someview'), + }; + }), }; }); @@ -32,6 +39,14 @@ jest.mock('@kbn/shared-ux-link-redirect-app', () => ({ }, })); +jest.mock('@kbn/logs-shared-plugin/common', () => { + return { + getLogsLocatorsFromUrlService: jest.fn().mockReturnValue({ + logsLocator: { getRedirectUrl: jest.fn(() => 'https://discover-redirect-url') }, + }), + }; +}); + const mockStartServices = (isServerlessEnabled?: boolean) => { mockUseStartServices.mockReturnValue({ application: {}, @@ -47,18 +62,27 @@ const mockStartServices = (isServerlessEnabled?: boolean) => { }, }, }, - http: { - basePath: { - prepend: (url: string) => 'http://localhost:5620' + url, + share: { + url: { + locators: { + get: () => ({ + useUrl: () => 'https://locator.url', + }), + }, }, }, - cloud: { - isServerlessEnabled, - }, }); }; describe('ViewErrors', () => { + beforeEach(() => { + jest.mocked(useAuthz).mockReturnValue({ + fleet: { + allAgents: true, + readAgents: true, + }, + } as any); + }); const renderComponent = (action: ActionStatus) => { return render( @@ -67,7 +91,7 @@ describe('ViewErrors', () => { ); }; - it('should render error message with btn to Logs view if serverless not enabled', () => { + it('should render error message with btn to Logs view', () => { mockStartServices(); const result = renderComponent({ actionId: 'action1', @@ -82,15 +106,32 @@ describe('ViewErrors', () => { const errorText = result.getByTestId('errorText'); expect(errorText.textContent).toEqual('Agent agent1 is not upgradeable'); + }); + + it('should render open in Logs button if correct privileges are set', () => { + mockStartServices(); + const result = renderComponent({ + actionId: 'action1', + latestErrors: [ + { + agentId: 'agent1', + error: 'Agent agent1 is not upgradeable', + timestamp: '2023-03-06T14:51:24.709Z', + }, + ], + } as any); const viewErrorBtn = result.getByTestId('viewInLogsBtn'); - expect(viewErrorBtn.getAttribute('href')).toEqual( - `http://localhost:5620/app/logs/stream?logPosition=(end%3A'2023-03-06T14%3A56%3A24.709Z'%2Cstart%3A'2023-03-06T14%3A46%3A24.709Z'%2CstreamLive%3A!f)&logFilter=(expression%3A'elastic_agent.id%3Aagent1%20and%20(data_stream.dataset%3Aelastic_agent)%20and%20(log.level%3Aerror)'%2Ckind%3Akuery)` - ); + expect(viewErrorBtn.getAttribute('href')).toEqual(`https://discover-redirect-url`); }); - it('should render error message with btn to Discover view if serverless enabled', () => { - mockStartServices(true); + it('should not render open in Logs button if privileges are not set', () => { + jest.mocked(useAuthz).mockReturnValue({ + fleet: { + readAgents: false, + }, + } as any); + mockStartServices(); const result = renderComponent({ actionId: 'action1', latestErrors: [ @@ -102,12 +143,6 @@ describe('ViewErrors', () => { ], } as any); - const errorText = result.getByTestId('errorText'); - expect(errorText.textContent).toEqual('Agent agent1 is not upgradeable'); - - const viewErrorBtn = result.getByTestId('viewInDiscoverBtn'); - expect(viewErrorBtn.getAttribute('href')).toEqual( - `http://localhost:5620/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:'2023-03-06T14:46:24.709Z',to:'2023-03-06T14:56:24.709Z'))&_a=(columns:!(event.dataset,message),index:'logs-*',query:(language:kuery,query:'elastic_agent.id:agent1 and (data_stream.dataset:elastic_agent) and (log.level:error)'))` - ); + expect(result.queryByTestId('viewInLogsBtn')).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.tsx index 4d43c9a60a618..e73d7778d1405 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/view_errors.tsx @@ -17,7 +17,10 @@ import { i18n } from '@kbn/i18n'; import type { ActionErrorResult } from '../../../../../../../common/types'; import { buildQuery } from '../../agent_details_page/components/agent_logs/build_query'; -import { ViewLogsButton } from '../../agent_details_page/components/agent_logs/view_logs_button'; +import { + ViewLogsButton, + getFormattedRange, +} from '../../agent_details_page/components/agent_logs/view_logs_button'; import type { ActionStatus } from '../../../../types'; import { useStartServices } from '../../../../hooks'; @@ -30,11 +33,12 @@ const TruncatedEuiText = styled(EuiText)` export const ViewErrors: React.FunctionComponent<{ action: ActionStatus }> = ({ action }) => { const coreStart = useStartServices(); - const isLogsUIAvailable = !coreStart.cloud?.isServerlessEnabled; - const getLogsButton = (agentId: string, timestamp: string, viewInLogs: boolean) => { - const startTime = moment(timestamp).subtract(5, 'm').toISOString(); - const endTime = moment(timestamp).add(5, 'm').toISOString(); + const getLogsButton = (agentId: string, timestamp: string) => { + const start = moment(timestamp).subtract(5, 'm').toISOString(); + const end = moment(timestamp).add(5, 'm').toISOString(); + const startTime = getFormattedRange(start); + const endTime = getFormattedRange(end); const logStreamQuery = buildQuery({ agentId, @@ -43,12 +47,7 @@ export const ViewErrors: React.FunctionComponent<{ action: ActionStatus }> = ({ userQuery: '', }); return ( - + ); }; @@ -86,7 +85,7 @@ export const ViewErrors: React.FunctionComponent<{ action: ActionStatus }> = ({ const errorItem = (action.latestErrors ?? []).find((item) => item.agentId === agentId); return ( - {getLogsButton(agentId, errorItem!.timestamp, !!isLogsUIAvailable)} + {getLogsButton(agentId, errorItem!.timestamp)} ); }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx index 3a21103503ae5..a9adc6140c7c5 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/enrollment_token_list_page/index.tsx @@ -128,6 +128,7 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { const agentPolicy = agentPoliciesById[enrollmentKey.policy_id]; return !agentPolicy?.is_managed; }) || []; + const filteredTotal = rowItems.length; const columns = [ { @@ -294,7 +295,7 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { pagination={{ pageIndex: pagination.currentPage - 1, pageSize: pagination.pageSize, - totalItemCount: total, + totalItemCount: filteredTotal, pageSizeOptions, }} onChange={({ page }: { page: { index: number; size: number } }) => { diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index cc91af6a873a8..aa86607a84ee5 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -230,7 +230,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps { field: 'packagePolicy.policy_ids', name: i18n.translate('xpack.fleet.epm.packageDetails.integrationList.agentPolicy', { - defaultMessage: 'Agent policy', + defaultMessage: 'Agent policies', }), truncateText: true, render(id, { agentPolicies, packagePolicy }) { diff --git a/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx b/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx index 35f2313f37e0a..6369d344a2d9f 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_delete_provider.tsx @@ -43,6 +43,16 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent = ({ const onSuccessCallback = useRef(null); const { canUseMultipleAgentPolicies } = useMultipleAgentPolicies(); + const isShared = useMemo(() => { + if (agentPolicies?.length !== 1) { + return false; + } + const packagePolicy = agentPolicies[0].package_policies?.find( + (policy) => policy.id === packagePolicies[0] + ); + return (packagePolicy?.policy_ids?.length ?? 0) > 1; + }, [agentPolicies, packagePolicies]); + const hasMultipleAgentPolicies = canUseMultipleAgentPolicies && agentPolicies && agentPolicies.length > 1; @@ -59,7 +69,7 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent = ({ const request = await sendGetAgents({ kuery, - showInactive: false, + showInactive: true, }); setAgentsCount(request.data?.total || 0); setIsLoadingAgentsCount(false); @@ -196,7 +206,7 @@ export const PackagePolicyDeleteProvider: React.FunctionComponent = ({ /> ) : agentsCount && agentPolicies ? ( <> - {hasMultipleAgentPolicies && ( + {(hasMultipleAgentPolicies || isShared) && ( <> { const { http, cloud } = useStartServices(); const isLogsUIAvailable = !cloud?.isServerlessEnabled; // if logs ui is not available, link to discover + // TODO: move away from hardcoded link and use locators instead const logStreamUrl = isLogsUIAvailable ? http.basePath.prepend('/app/logs/stream') : http.basePath.prepend('/app/discover'); diff --git a/x-pack/plugins/fleet/public/hooks/use_locator.ts b/x-pack/plugins/fleet/public/hooks/use_locator.ts index a3fed97679456..25a673b694670 100644 --- a/x-pack/plugins/fleet/public/hooks/use_locator.ts +++ b/x-pack/plugins/fleet/public/hooks/use_locator.ts @@ -21,3 +21,7 @@ export function useLocator( export function useDashboardLocator() { return useLocator(LOCATORS_IDS.DASHBOARD_APP); } + +export function useDiscoverLocator() { + return useLocator(LOCATORS_IDS.DISCOVER_APP_LOCATOR); +} diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index a26fc6760397c..2fe60943b1909 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -376,10 +376,43 @@ describe('When calling package policy', () => { it('should not throw if enterprise license and multiple policy_ids is provided', async () => { jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + jest + .spyOn(appContextService, 'getExperimentalFeatures') + .mockReturnValue({ enableReusableIntegrationPolicies: true } as any); const request = getUpdateKibanaRequest({ policy_ids: ['1', '2'] } as any); await routeHandler(context, request, response); expect(response.ok).toHaveBeenCalled(); }); + + it('should throw if enterprise license and feature flag is disabled and multiple policy_ids is provided', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + jest + .spyOn(appContextService, 'getExperimentalFeatures') + .mockReturnValue({ enableReusableIntegrationPolicies: false } as any); + const request = getUpdateKibanaRequest({ policy_ids: ['1', '2'] } as any); + await routeHandler(context, request, response); + expect(response.customError).toHaveBeenCalledWith({ + statusCode: 400, + body: { + message: 'Reusable integration policies are not supported', + }, + }); + }); + + it('should throw if empty policy_ids are provided', async () => { + jest.spyOn(licenseService, 'hasAtLeast').mockReturnValue(true); + jest + .spyOn(appContextService, 'getExperimentalFeatures') + .mockReturnValue({ enableReusableIntegrationPolicies: true } as any); + const request = getUpdateKibanaRequest({ policy_ids: [] } as any); + await routeHandler(context, request, response); + expect(response.customError).toHaveBeenCalledWith({ + statusCode: 400, + body: { + message: 'At least one agent policy id must be provided', + }, + }); + }); }); describe('list api handler', () => { diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index abad84ef9db9d..a40b2a41d89db 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -421,6 +421,10 @@ export const updatePackagePolicyHandler: FleetRequestHandler< throw new PackagePolicyRequestError(errorMessage); } + if (newData.policy_ids && newData.policy_ids.length === 0) { + throw new PackagePolicyRequestError('At least one agent policy id must be provided'); + } + const updatedPackagePolicy = await packagePolicyService.update( soClient, esClient, diff --git a/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts b/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts index da1fca175b9cf..7a88899d8ba28 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/utils/index.ts @@ -8,7 +8,7 @@ import type { TypeOf } from '@kbn/config-schema'; import type { CreatePackagePolicyRequestSchema, PackagePolicyInput } from '../../../types'; -import { licenseService } from '../../../services'; +import { appContextService, licenseService } from '../../../services'; import type { SimplifiedPackagePolicy } from '../../../../common/services/simplified_package_policy_helper'; export function isSimplifiedCreatePackagePolicyRequest( @@ -44,9 +44,12 @@ const LICENCE_FOR_MULTIPLE_AGENT_POLICIES = 'enterprise'; export function canUseMultipleAgentPolicies() { const hasEnterpriseLicence = licenseService.hasAtLeast(LICENCE_FOR_MULTIPLE_AGENT_POLICIES); + const { enableReusableIntegrationPolicies } = appContextService.getExperimentalFeatures(); return { - canUseReusablePolicies: hasEnterpriseLicence, - errorMessage: 'Reusable integration policies are only available with an Enterprise license', + canUseReusablePolicies: hasEnterpriseLicence && enableReusableIntegrationPolicies, + errorMessage: !hasEnterpriseLicence + ? 'Reusable integration policies are only available with an Enterprise license' + : 'Reusable integration policies are not supported', }; } diff --git a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts index 8e47180076338..6eff632c06abb 100644 --- a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts +++ b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts @@ -42,7 +42,7 @@ spec: # - -c # - >- # mkdir -p /usr/share/elastic-agent/state/inputs.d && - # curl -sL https://github.com/elastic/elastic-agent/archive/8.15.tar.gz | tar xz -C /usr/share/elastic-agent/state/inputs.d --strip=5 "elastic-agent-8.15/deploy/kubernetes/elastic-agent/templates.d" + # curl -sL https://github.com/elastic/elastic-agent/archive/8.16.tar.gz | tar xz -C /usr/share/elastic-agent/state/inputs.d --strip=5 "elastic-agent-8.16/deploy/kubernetes/elastic-agent/templates.d" # securityContext: # runAsUser: 0 # volumeMounts: @@ -351,9 +351,6 @@ spec: effect: NoSchedule serviceAccountName: elastic-agent hostNetwork: true - # 'hostPID: true' enables the Elastic Security integration to observe all process exec events on the host. - # Sharing the host process ID namespace gives visibility of all processes running on the same host. - hostPID: true dnsPolicy: ClusterFirstWithHostNet containers: - name: elastic-agent @@ -469,7 +466,7 @@ spec: hostPath: path: /var/lib # Mount /etc/machine-id from the host to determine host ID - # Needed for Elastic Security integration + # Needed for Kubernetes node autodiscovery - name: etc-mid hostPath: path: /etc/machine-id diff --git a/x-pack/plugins/fleet/server/services/fleet_server/index.test.ts b/x-pack/plugins/fleet/server/services/fleet_server/index.test.ts index f00d78cd59ad9..7faea8c526819 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server/index.test.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server/index.test.ts @@ -115,6 +115,13 @@ describe('checkFleetServerVersionsForSecretsStorage', () => { version ); expect(result).toBe(true); + expect(mockedGetAgentsByKuery).toHaveBeenCalledWith( + esClientMock, + soClientMock, + expect.objectContaining({ + kuery: 'policy_id:("1" or "2")', + }) + ); }); }); diff --git a/x-pack/plugins/fleet/server/services/fleet_server/index.ts b/x-pack/plugins/fleet/server/services/fleet_server/index.ts index 004a0deeea7b7..a0d508f0929e9 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server/index.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server/index.ts @@ -128,11 +128,19 @@ export async function checkFleetServerVersionsForSecretsStorage( hasMore = false; } } + if (policyIds.size === 0) { + return false; + } + + const kuery = `policy_id:(${Array.from(policyIds) + .map((id) => `"${id}"`) + .join(' or ')})`; const managedAgentPolicies = await agentPolicyService.getAllManagedAgentPolicies(soClient); const fleetServerAgents = await getAgentsByKuery(esClient, soClient, { showInactive: true, perPage: SO_SEARCH_LIMIT, + kuery, }); if (fleetServerAgents.agents.length === 0) { diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 8986527bed977..b45bd90010b42 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -64,7 +64,6 @@ "@kbn/utility-types-jest", "@kbn/es-query", "@kbn/ui-theme", - "@kbn/rison", "@kbn/config-schema", "@kbn/telemetry-plugin", "@kbn/task-manager-plugin", diff --git a/x-pack/plugins/global_search_providers/public/providers/get_app_results.ts b/x-pack/plugins/global_search_providers/public/providers/get_app_results.ts index f83c6405369b7..fdbde1c58de81 100644 --- a/x-pack/plugins/global_search_providers/public/providers/get_app_results.ts +++ b/x-pack/plugins/global_search_providers/public/providers/get_app_results.ts @@ -5,8 +5,8 @@ * 2.0. */ -import levenshtein from 'js-levenshtein'; import { PublicAppInfo, PublicAppDeepLinkInfo, AppCategory } from '@kbn/core/public'; +import { distance } from 'fastest-levenshtein'; import { GlobalSearchProviderResult } from '@kbn/global-search-plugin/public'; /** Type used internally to represent an application unrolled into its separate deepLinks */ @@ -80,10 +80,10 @@ const scoreAppByTerms = (term: string, title: string): number => { return 75; } const length = Math.max(term.length, title.length); - const distance = levenshtein(term, title); + const dist = distance(term, title); // maximum lev distance is length, we compute the match ratio (lower distance is better) - const ratio = Math.floor((1 - distance / length) * 100); + const ratio = Math.floor((1 - dist / length) * 100); if (ratio >= 60) { return ratio; } diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index 19490125e8840..633ed96ec8225 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -86,7 +86,7 @@ const appDependencies = { editableIndexSettings: 'all', enableMappingsSourceFieldSection: true, enableTogglingDataRetention: true, - enableSemanticText: false, + enableSemanticText: true, }, overlays: { openConfirm: jest.fn(), diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts index 91dd4d26c2a5b..d80b08b96dacc 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.helpers.ts @@ -61,7 +61,7 @@ export interface IndexDetailsPageTestBed extends TestBed { selectInferenceIdButtonExists: () => void; openSelectInferencePopover: () => void; expectDefaultInferenceModelToExists: () => void; - expectCustomInferenceModelToExists: (customInference: string) => Promise; + expectCustomInferenceModelToExists: (customInference: string) => void; }; settings: { getCodeBlockContent: () => string; @@ -317,23 +317,23 @@ export const setup = async ({ expect(exists('fieldTypesOptions-semantic_text')).toBe(false); }); }, - isReferenceFieldVisible: async () => { - expect(exists('referenceField.select')).toBe(true); + isReferenceFieldVisible: () => { + expect(exists('referenceFieldSelect')).toBe(true); }, - selectInferenceIdButtonExists: async () => { + selectInferenceIdButtonExists: () => { expect(exists('selectInferenceId')).toBe(true); expect(exists('inferenceIdButton')).toBe(true); find('inferenceIdButton').simulate('click'); }, - openSelectInferencePopover: async () => { + openSelectInferencePopover: () => { expect(exists('addInferenceEndpointButton')).toBe(true); expect(exists('manageInferenceEndpointButton')).toBe(true); }, - expectDefaultInferenceModelToExists: async () => { - expect(exists('default-inference_elser_model_2')).toBe(true); - expect(exists('default-inference_e5')).toBe(true); + expectDefaultInferenceModelToExists: () => { + expect(exists('custom-inference_elser_model_2')).toBe(true); + expect(exists('custom-inference_e5')).toBe(true); }, - expectCustomInferenceModelToExists: async (customInference: string) => { + expectCustomInferenceModelToExists: (customInference: string) => { expect(exists(customInference)).toBe(true); }, }; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx index c79efbbf53f53..298ad1f9af02c 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/index_details_page.test.tsx @@ -69,6 +69,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadIndexStatsResponse(testIndexName, testIndexStats); httpRequestsMockHelpers.setLoadIndexMappingResponse(testIndexName, testIndexMappings); httpRequestsMockHelpers.setLoadIndexSettingsResponse(testIndexName, testIndexSettings); + httpRequestsMockHelpers.setInferenceModels([]); await act(async () => { testBed = await setup({ @@ -671,7 +672,6 @@ describe('', () => { testBed = await setup({ httpSetup, dependencies: { - config: { enableSemanticText: true }, docLinks: { links: { ml: '', @@ -693,6 +693,7 @@ describe('', () => { ml: { mlApi: { trainedModels: { + getModelsDownloadStatus: jest.fn().mockResolvedValue({}), getTrainedModels: jest.fn().mockResolvedValue([ { model_id: '.elser_model_2', diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/select_inference_id.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/select_inference_id.test.tsx index c734943ec4592..43bf8073de0aa 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/select_inference_id.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/select_inference_id.test.tsx @@ -5,13 +5,20 @@ * 2.0. */ +import { + Form, + useForm, +} from '../../../public/application/components/mappings_editor/shared_imports'; import { registerTestBed } from '@kbn/test-jest-helpers'; import { act } from 'react-dom/test-utils'; -import { SelectInferenceId } from '../../../public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id'; +import { + SelectInferenceId, + SelectInferenceIdProps, +} from '../../../public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id'; +import React from 'react'; -const onChangeMock = jest.fn(); -const setValueMock = jest.fn(); -const setNewInferenceEndpointMock = jest.fn(); +const createInferenceEndpointMock = jest.fn(); +const mockDispatch = jest.fn(); jest.mock('../../../public/application/app_context', () => ({ useAppContext: jest.fn().mockReturnValue({ @@ -41,23 +48,40 @@ jest.mock( }), }) ); + +jest.mock('../../../public/application/components/mappings_editor/mappings_state_context', () => ({ + useMappingsState: () => ({ inferenceToModelIdMap: {} }), + useDispatch: () => mockDispatch, +})); + +function getTestForm(Component: React.FC) { + return (defaultProps: SelectInferenceIdProps) => { + const { form } = useForm(); + form.setFieldValue('inference_id', 'elser_model_2'); + return ( +
+ + + ); + }; +} + describe('SelectInferenceId', () => { let exists: any; let find: any; beforeAll(async () => { - const setup = registerTestBed(SelectInferenceId, { - defaultProps: { - onChange: onChangeMock, - 'data-test-subj': 'data-inference-endpoint-list', - setValue: setValueMock, - setNewInferenceEndpoint: setNewInferenceEndpointMock, - }, + const defaultProps: SelectInferenceIdProps = { + 'data-test-subj': 'data-inference-endpoint-list', + createInferenceEndpoint: createInferenceEndpointMock, + }; + const setup = registerTestBed(getTestForm(SelectInferenceId), { + defaultProps, memoryRouter: { wrapComponent: false }, }); await act(async () => { - const testBed = setup(); + const testBed = await setup(); exists = testBed.exists; find = testBed.find; }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/trained_models_deployment_modal.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/trained_models_deployment_modal.test.tsx index 17299a0f04b4f..cd9d099ee92f8 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/trained_models_deployment_modal.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_details_page/trained_models_deployment_modal.test.tsx @@ -6,26 +6,159 @@ */ import { registerTestBed } from '@kbn/test-jest-helpers'; -import { TrainedModelsDeploymentModal } from '../../../public/application/sections/home/index_list/details_page/trained_models_deployment_modal'; +import { + TrainedModelsDeploymentModal, + TrainedModelsDeploymentModalProps, +} from '../../../public/application/sections/home/index_list/details_page/trained_models_deployment_modal'; import { act } from 'react-dom/test-utils'; +import * as mappingsContext from '../../../public/application/components/mappings_editor/mappings_state_context'; +import { NormalizedField } from '../../../public/application/components/mappings_editor/types'; -const refreshModal = jest.fn(); -const setIsModalVisible = jest.fn(); -const tryAgainForErrorModal = jest.fn(); -const setIsVisibleForErrorModal = jest.fn(); +jest.mock('../../../public/hooks/use_ml_model_status_toasts', () => ({ + useMLModelNotificationToasts: jest.fn().mockReturnValue({ + showErrorToasts: jest.fn(), + }), +})); -describe('When semantic_text is enabled', () => { - describe('When there is no error in the model deployment', () => { - const setup = registerTestBed(TrainedModelsDeploymentModal, { - defaultProps: { - setIsModalVisible, - refreshModal, - pendingDeployments: ['.elser-test-3'], - errorsInTrainedModelDeployment: [], +jest.mock('../../../public/application/app_context', () => ({ + useAppContext: jest.fn().mockReturnValue({ + url: undefined, + plugins: { + ml: { + mlApi: { + trainedModels: { + getModelsDownloadStatus: jest.fn().mockResolvedValue({}), + getTrainedModels: jest.fn().mockResolvedValue([ + { + model_id: '.elser_model_2', + model_type: 'pytorch', + model_package: { + packaged_model_id: 'elser_model_2', + model_repository: 'https://ml-models.elastic.co', + minimum_version: '11.0.0', + size: 438123914, + sha256: '', + metadata: {}, + tags: [], + vocabulary_file: 'elser_model_2.vocab.json', + }, + description: 'Elastic Learned Sparse EncodeR v2', + tags: ['elastic'], + }, + ]), + getTrainedModelStats: jest.fn().mockResolvedValue({ + count: 1, + trained_model_stats: [ + { + model_id: '.elser_model_2', + + deployment_stats: { + deployment_id: 'elser_model_2', + model_id: '.elser_model_2', + threads_per_allocation: 1, + number_of_allocations: 1, + queue_capacity: 1024, + state: 'started', + }, + }, + ], + }), + }, + }, }, + }, + }), +})); + +jest.mock('../../../public/application/components/mappings_editor/mappings_state_context'); + +const mappingsContextMocked = jest.mocked(mappingsContext); + +const defaultState = { + inferenceToModelIdMap: { + e5: { + isDeployed: false, + isDeployable: true, + trainedModelId: '.multilingual-e5-small', + }, + elser_model_2: { + isDeployed: false, + isDeployable: true, + trainedModelId: '.elser_model_2', + }, + openai: { + isDeployed: false, + isDeployable: false, + trainedModelId: '', + }, + my_elser_endpoint: { + isDeployed: false, + isDeployable: true, + trainedModelId: '.elser_model_2', + }, + }, + fields: { + aliases: {}, + byId: {}, + rootLevelFields: [], + maxNestedDepth: 0, + }, + mappingViewFields: { byId: {} }, +} as any; + +const setErrorsInTrainedModelDeployment = jest.fn().mockReturnValue(undefined); +const fetchData = jest.fn().mockReturnValue(undefined); + +describe('When semantic_text is enabled', () => { + const setup = (defaultProps: Partial) => + registerTestBed(TrainedModelsDeploymentModal, { + defaultProps, memoryRouter: { wrapComponent: false }, + })(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('When there are no pending deployments and no errors in the model deployment', () => { + mappingsContextMocked.useMappingsState.mockReturnValue(defaultState); + const { exists } = setup({ + errorsInTrainedModelDeployment: {}, + fetchData, + setErrorsInTrainedModelDeployment: () => undefined, + }); + + it('should not display the modal', () => { + expect(exists('trainedModelsDeploymentModal')).toBe(false); + }); + }); + + describe('When there are pending deployments in the model deployment', () => { + mappingsContextMocked.useMappingsState.mockReturnValue({ + ...defaultState, + fields: { + ...defaultState.fields, + byId: { + new_field: { + id: 'new_field', + isMultiField: false, + path: ['new_field'], + source: { + name: 'new_field', + type: 'semantic_text', + reference_field: 'title', + inference_id: 'elser_model_2', + }, + } as NormalizedField, + }, + rootLevelFields: ['new_field'], + }, + } as any); + const { exists, find } = setup({ + errorsInTrainedModelDeployment: {}, + fetchData, + setErrorsInTrainedModelDeployment, }); - const { exists, find } = setup(); it('should display the modal', () => { expect(exists('trainedModelsDeploymentModal')).toBe(true); @@ -37,55 +170,61 @@ describe('When semantic_text is enabled', () => { ); }); - it('should call refresh method if refresh button is pressed', async () => { + it('should call fetch data if refresh button is pressed', async () => { await act(async () => { find('confirmModalConfirmButton').simulate('click'); }); - expect(refreshModal.mock.calls).toHaveLength(1); - }); - - it('should call setIsModalVisible method if cancel button is pressed', async () => { - await act(async () => { - find('confirmModalCancelButton').simulate('click'); - }); - expect(setIsModalVisible).toHaveBeenLastCalledWith(false); + expect(fetchData.mock.calls).toHaveLength(1); }); }); describe('When there is error in the model deployment', () => { - const setup = registerTestBed(TrainedModelsDeploymentModal, { - defaultProps: { - setIsModalVisible: setIsVisibleForErrorModal, - refreshModal: tryAgainForErrorModal, - pendingDeployments: ['.elser-test-3'], - errorsInTrainedModelDeployment: ['.elser-test-3'], + mappingsContextMocked.useMappingsState.mockReturnValue({ + ...defaultState, + fields: { + ...defaultState.fields, + byId: { + new_field: { + id: 'new_field', + isMultiField: false, + path: ['new_field'], + source: { + name: 'new_field', + type: 'semantic_text', + reference_field: 'title', + inference_id: 'elser_model_2', + }, + } as NormalizedField, + }, + rootLevelFields: ['new_field'], }, - memoryRouter: { wrapComponent: false }, + } as any); + const { find } = setup({ + fetchData, + errorsInTrainedModelDeployment: { '.elser_model_2': 'Error' }, + setErrorsInTrainedModelDeployment, }); - const { exists, find } = setup(); - it('should display the modal', () => { - expect(exists('trainedModelsErroredDeploymentModal')).toBe(true); + it('should display text related to errored deployments', () => { + expect(find('trainedModelsDeploymentModalText').text()).toContain('There was an error'); }); - it('should contain content related to semantic_text', () => { - expect(find('trainedModelsErrorDeploymentModalText').text()).toContain( - 'There was an error when trying to deploy' - ); + it('should display only the errored deployment', () => { + expect(find('trainedModelsDeploymentModal').text()).toContain('.elser_model_2'); + expect(find('trainedModelsDeploymentModal').text()).not.toContain('valid-model'); }); it("should call refresh method if 'Try again' button is pressed", async () => { await act(async () => { find('confirmModalConfirmButton').simulate('click'); }); - expect(tryAgainForErrorModal.mock.calls).toHaveLength(1); + expect(fetchData.mock.calls).toHaveLength(1); }); it('should call setIsVisibleForErrorModal method if cancel button is pressed', async () => { await act(async () => { find('confirmModalCancelButton').simulate('click'); }); - expect(setIsVisibleForErrorModal).toHaveBeenLastCalledWith(false); }); }); }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx index eb9e6c793aa5a..dac7dadfa2557 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx @@ -12,7 +12,13 @@ import * as fixtures from '../../../test/fixtures'; import { API_BASE_PATH } from '../../../common/constants'; import { setupEnvironment, kibanaVersion } from '../helpers'; -import { TEMPLATE_NAME, SETTINGS, ALIASES, MAPPINGS as DEFAULT_MAPPING } from './constants'; +import { + TEMPLATE_NAME, + SETTINGS, + ALIASES, + MAPPINGS as DEFAULT_MAPPING, + INDEX_PATTERNS, +} from './constants'; import { setup } from './template_edit.helpers'; import { TemplateFormTestBed } from './template_form.helpers'; @@ -26,6 +32,22 @@ const MAPPING = { }, }, }; +const NONEXISTENT_COMPONENT_TEMPLATE = { + name: 'component_template@custom', + hasMappings: false, + hasAliases: false, + hasSettings: false, + usedBy: [], +}; + +const EXISTING_COMPONENT_TEMPLATE = { + name: 'test_component_template', + hasMappings: true, + hasAliases: false, + hasSettings: false, + usedBy: [], + isManaged: false, +}; jest.mock('@kbn/code-editor', () => { const original = jest.requireActual('@kbn/code-editor'); @@ -70,6 +92,7 @@ describe('', () => { beforeAll(() => { jest.useFakeTimers({ legacyFakeTimers: true }); httpRequestsMockHelpers.setLoadComponentTemplatesResponse([]); + httpRequestsMockHelpers.setLoadComponentTemplatesResponse([EXISTING_COMPONENT_TEMPLATE]); }); afterAll(() => { @@ -296,6 +319,84 @@ describe('', () => { }); }); + describe('when composed of a nonexistent component template', () => { + const templateToEdit = fixtures.getTemplate({ + name: TEMPLATE_NAME, + indexPatterns: INDEX_PATTERNS, + composedOf: [NONEXISTENT_COMPONENT_TEMPLATE.name], + ignoreMissingComponentTemplates: [NONEXISTENT_COMPONENT_TEMPLATE.name], + }); + + beforeAll(() => { + httpRequestsMockHelpers.setLoadTemplateResponse('my_template', templateToEdit); + }); + + beforeEach(async () => { + await act(async () => { + testBed = await setup(httpSetup); + }); + testBed.component.update(); + }); + + it('the nonexistent component template should be selected in the Component templates selector', async () => { + const { actions, exists } = testBed; + + // Complete step 1: Logistics + await actions.completeStepOne(); + jest.advanceTimersByTime(0); // advance timers to allow the form to validate + + // Should be at the Component templates step + expect(exists('stepComponents')).toBe(true); + + const { + actions: { + componentTemplates: { getComponentTemplatesSelected }, + }, + } = testBed; + + expect(exists('componentTemplatesSelection.emptyPrompt')).toBe(false); + expect(getComponentTemplatesSelected()).toEqual([NONEXISTENT_COMPONENT_TEMPLATE.name]); + }); + + it('the composedOf and ignoreMissingComponentTemplates fields should be included in the final payload', async () => { + const { component, actions, find } = testBed; + + // Complete step 1: Logistics + await actions.completeStepOne(); + // Complete step 2: Component templates + await actions.completeStepTwo(); + // Complete step 3: Index settings + await actions.completeStepThree(); + // Complete step 4: Mappings + await actions.completeStepFour(); + // Complete step 5: Aliases + await actions.completeStepFive(); + + expect(find('stepTitle').text()).toEqual(`Review details for '${TEMPLATE_NAME}'`); + + await act(async () => { + actions.clickNextButton(); + }); + component.update(); + + expect(httpSetup.put).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/index_templates/${TEMPLATE_NAME}`, + expect.objectContaining({ + body: JSON.stringify({ + name: TEMPLATE_NAME, + indexPatterns: INDEX_PATTERNS, + version: templateToEdit.version, + allowAutoCreate: templateToEdit.allowAutoCreate, + _kbnMeta: templateToEdit._kbnMeta, + composedOf: [NONEXISTENT_COMPONENT_TEMPLATE.name], + template: {}, + ignoreMissingComponentTemplates: [NONEXISTENT_COMPONENT_TEMPLATE.name], + }), + }) + ); + }); + }); + if (kibanaVersion.major < 8) { describe('legacy index templates', () => { const legacyTemplateToEdit = fixtures.getTemplate({ diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.tsx index 71c2f09072dac..1c727265fd1f1 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_selector/component_templates_selector.tsx @@ -87,8 +87,20 @@ export const ComponentTemplatesSelector = ({ .map((name) => components.find((comp) => comp.name === name)) .filter(Boolean) as ComponentTemplateListItem[]; - setComponentsSelected(nextComponentsSelected); - onChange(nextComponentsSelected.map(({ name }) => name)); + // Add the non-existing templates from the "defaultValue" prop + const missingDefaultComponents: ComponentTemplateListItem[] = defaultValue + .filter((name) => !components.find((comp) => comp.name === name)) + .map((name) => ({ + name, + usedBy: [], + hasMappings: false, + hasAliases: false, + hasSettings: false, + isManaged: false, + })); + + setComponentsSelected([...nextComponentsSelected, ...missingDefaultComponents]); + onChange([...nextComponentsSelected, ...missingDefaultComponents].map(({ name }) => name)); isInitialized.current = true; } else { onChange(componentsSelected.map(({ name }) => name)); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx index e1c3ea7dc6179..094fd40ab1e32 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx @@ -380,8 +380,11 @@ export const setup = ( props: any = { onUpdate() {} }, appDependencies?: any ): MappingsEditorTestBed => { + const defaultAppDependencies = { + plugins: {}, + }; const setupTestBed = registerTestBed( - WithAppDependencies(MappingsEditor, appDependencies), + WithAppDependencies(MappingsEditor, appDependencies ?? defaultAppDependencies), { memoryRouter: { wrapComponent: false, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx index 1347aaeade4f8..685aa4963edc4 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx @@ -28,6 +28,7 @@ describe('Mappings editor: core', () => { let onChangeHandler: jest.Mock = jest.fn(); let getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler); let testBed: MappingsEditorTestBed; + const appDependencies = { plugins: { ml: { mlApi: {} } } }; beforeAll(() => { jest.useFakeTimers({ legacyFakeTimers: true }); @@ -55,7 +56,7 @@ describe('Mappings editor: core', () => { }; await act(async () => { - testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); + testBed = setup({ value: defaultMappings, onChange: onChangeHandler }, appDependencies); }); const { component } = testBed; @@ -95,7 +96,7 @@ describe('Mappings editor: core', () => { }; await act(async () => { - testBed = setup({ onChange: onChangeHandler, value }); + testBed = setup({ onChange: onChangeHandler, value }, appDependencies); }); const { component, exists } = testBed; @@ -115,7 +116,7 @@ describe('Mappings editor: core', () => { }, }; await act(async () => { - testBed = setup({ onChange: onChangeHandler, value }); + testBed = setup({ onChange: onChangeHandler, value }, appDependencies); }); const { component, exists } = testBed; @@ -137,6 +138,7 @@ describe('Mappings editor: core', () => { config: { enableMappingsSourceFieldSection: true, }, + ...appDependencies, }; beforeEach(async () => { @@ -295,6 +297,7 @@ describe('Mappings editor: core', () => { config: { enableMappingsSourceFieldSection: true, }, + ...appDependencies, }; beforeEach(async () => { @@ -472,7 +475,7 @@ describe('Mappings editor: core', () => { }, }; await act(async () => { - testBed = setup({ onChange: onChangeHandler, value }); + testBed = setup({ onChange: onChangeHandler, value }, appDependencies); }); const { component, exists } = testBed; @@ -494,7 +497,7 @@ describe('Mappings editor: core', () => { }, }; await act(async () => { - testBed = setup({ onChange: onChangeHandler, value }); + testBed = setup({ onChange: onChangeHandler, value }, appDependencies); }); const { component } = testBed; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx index e895a00b9f6d8..6fe58e7ba26da 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx @@ -7,6 +7,7 @@ import React, { useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; import { TextField, UseField, FieldConfig } from '../../../shared_imports'; import { validateUniqueName } from '../../../lib'; import { PARAMETERS_DEFINITION } from '../../../constants'; @@ -14,7 +15,11 @@ import { useMappingsState } from '../../../mappings_state_context'; const { validations, ...rest } = PARAMETERS_DEFINITION.name.fieldConfig as FieldConfig; -export const NameParameter = () => { +interface NameParameterProps { + isSemanticText?: boolean; +} + +export const NameParameter: React.FC = ({ isSemanticText }) => { const { fields: { rootLevelFields, byId }, documentFields: { fieldToAddFieldTo, fieldToEdit }, @@ -32,6 +37,11 @@ export const NameParameter = () => { const nameConfig: FieldConfig = useMemo( () => ({ ...rest, + label: isSemanticText + ? i18n.translate('xpack.idxMgmt.mappingsEditor.semanticTextNameFieldLabel', { + defaultMessage: 'New field name', + }) + : rest.label, validations: [ ...validations!, { @@ -39,7 +49,7 @@ export const NameParameter = () => { }, ], }), - [uniqueNameValidator] + [isSemanticText, uniqueNameValidator] ); return ( diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/reference_field_selects.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/reference_field_selects.tsx index b8f5e866b68a9..9d923f529931b 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/reference_field_selects.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/reference_field_selects.tsx @@ -5,65 +5,45 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; -import { useLoadIndexMappings } from '../../../../../services'; import { getFieldConfig } from '../../../lib'; -import { Form, SuperSelectField, UseField, useForm } from '../../../shared_imports'; +import { useMappingsState } from '../../../mappings_state_context'; +import { SuperSelectField, UseField } from '../../../shared_imports'; import { SuperSelectOption } from '../../../types'; -interface Props { - onChange(value: string): void; - 'data-test-subj'?: string; - indexName?: string; -} +export const ReferenceFieldSelects = () => { + const { fields, mappingViewFields } = useMappingsState(); -export const ReferenceFieldSelects = ({ - onChange, - 'data-test-subj': dataTestSubj, - indexName, -}: Props) => { - const { form } = useForm(); - const { subscribe } = form; + const allFields = { + byId: { + ...mappingViewFields.byId, + ...fields.byId, + }, + rootLevelFields: [], + aliases: {}, + maxNestedDepth: 0, + }; - const { data } = useLoadIndexMappings(indexName ?? ''); - const referenceFieldOptions: SuperSelectOption[] = []; - if (data && data.mappings && data.mappings.properties) { - Object.keys(data.mappings.properties).forEach((key) => { - const field = data.mappings.properties[key]; - if (field.type === 'text') { - referenceFieldOptions.push({ - value: key, - inputDisplay: key, - 'data-test-subj': `select-reference-field-${key}`, - }); - } - }); - } + const referenceFieldOptions: SuperSelectOption[] = Object.values(allFields.byId) + .filter((field) => field.source.type === 'text') + .map((field) => ({ + value: field.path.join('.'), + inputDisplay: field.path.join('.'), + 'data-test-subj': `select-reference-field-${field.path.join('.')}}`, + })); const fieldConfigReferenceField = getFieldConfig('reference_field'); - - useEffect(() => { - const subscription = subscribe((updateData) => { - const formData = updateData.data.internal; - const value = formData.main; - onChange(value); - }); - - return subscription.unsubscribe; - }, [subscribe, onChange]); return ( -
- - {(field) => ( - - )} - -
+ + {(field) => ( + + )} + ); }; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx index e9a4387f91206..c4da2d30ac4fd 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx @@ -24,63 +24,74 @@ import { import { i18n } from '@kbn/i18n'; import React, { useEffect, useState, useCallback, useMemo } from 'react'; -import { - InferenceAPIConfigResponse, - SUPPORTED_PYTORCH_TASKS, - TRAINED_MODEL_TYPE, -} from '@kbn/ml-trained-models-utils'; +import { SUPPORTED_PYTORCH_TASKS, TRAINED_MODEL_TYPE } from '@kbn/ml-trained-models-utils'; import { InferenceTaskType } from '@elastic/elasticsearch/lib/api/types'; -import { - ElasticsearchModelDefaultOptions, - ModelConfig, - Service, -} from '@kbn/inference_integration_flyout/types'; +import { ModelConfig } from '@kbn/inference_integration_flyout/types'; import { InferenceFlyoutWrapper } from '@kbn/inference_integration_flyout/components/inference_flyout_wrapper'; import { TrainedModelConfigResponse } from '@kbn/ml-plugin/common/types/trained_models'; import { getFieldConfig } from '../../../lib'; import { useAppContext } from '../../../../../app_context'; -import { Form, UseField, useForm } from '../../../shared_imports'; import { useLoadInferenceEndpoints } from '../../../../../services/api'; -import { getTrainedModelStats } from '../../../../../../hooks/use_details_page_mappings_model_management'; -import { InferenceToModelIdMap } from '../fields'; import { useMLModelNotificationToasts } from '../../../../../../hooks/use_ml_model_status_toasts'; -import { - CustomInferenceEndpointConfig, - DefaultInferenceModels, - DeploymentState, -} from '../../../types'; +import { CustomInferenceEndpointConfig } from '../../../types'; +import { UseField } from '../../../shared_imports'; -const inferenceServiceTypeElasticsearchModelMap: Record = - { - elser: ElasticsearchModelDefaultOptions.elser, - elasticsearch: ElasticsearchModelDefaultOptions.e5, - }; -const uncheckSelectedModelOption = (options: EuiSelectableOption[]) => { - const checkedOption = options.find(({ checked }) => checked === 'on'); - if (checkedOption) { - checkedOption.checked = undefined; - } -}; -interface Props { - onChange(value: string): void; +export interface SelectInferenceIdProps { + createInferenceEndpoint: ( + trainedModelId: string, + inferenceId: string, + modelConfig: CustomInferenceEndpointConfig + ) => Promise; 'data-test-subj'?: string; - setValue: (value: string) => void; - setNewInferenceEndpoint: ( - newInferenceEndpoint: InferenceToModelIdMap, - customInferenceEndpointConfig: CustomInferenceEndpointConfig - ) => void; } -export const SelectInferenceId = ({ - onChange, + +type SelectInferenceIdContentProps = SelectInferenceIdProps & { + setValue: (value: string) => void; + value: string; +}; + +const defaultEndpoints = [ + { + model_id: 'elser_model_2', + }, + { + model_id: 'e5', + }, +]; + +export const SelectInferenceId: React.FC = ({ + createInferenceEndpoint, + 'data-test-subj': dataTestSubj, +}: SelectInferenceIdProps) => { + const config = getFieldConfig('inference_id'); + return ( + + {(field) => { + return ( + + ); + }} + + ); +}; + +const SelectInferenceIdContent: React.FC = ({ + createInferenceEndpoint, 'data-test-subj': dataTestSubj, setValue, - setNewInferenceEndpoint, -}: Props) => { + value, +}) => { const { core: { application }, docLinks, plugins: { ml }, } = useAppContext(); + const config = getFieldConfig('inference_id'); const getMlTrainedModelPageUrl = useCallback(async () => { return await ml?.locator?.getUrl({ @@ -88,9 +99,6 @@ export const SelectInferenceId = ({ }); }, [ml]); - const { form } = useForm({ defaultValue: { main: DefaultInferenceModels.elser_model_2 } }); - const { subscribe } = form; - const [isInferenceFlyoutVisible, setIsInferenceFlyoutVisible] = useState(false); const [availableTrainedModels, setAvailableTrainedModels] = useState< TrainedModelConfigResponse[] @@ -118,101 +126,57 @@ export const SelectInferenceId = ({ return availableTrainedModelsList; }, [availableTrainedModels]); + const [isSaveInferenceLoading, setIsSaveInferenceLoading] = useState(false); - const fieldConfigModelId = getFieldConfig('inference_id'); - const defaultInferenceIds: EuiSelectableOption[] = useMemo(() => { - return [ - { - checked: 'on', - label: 'elser_model_2', - 'data-test-subj': 'default-inference_elser_model_2', - }, - { - label: 'e5', - 'data-test-subj': 'default-inference_e5', - }, - ]; - }, []); - - const { isLoading, data: models } = useLoadInferenceEndpoints(); - - const [options, setOptions] = useState([...defaultInferenceIds]); - const inferenceIdOptionsFromModels = useMemo(() => { - const inferenceIdOptions = - models?.map((model: InferenceAPIConfigResponse) => ({ - label: model.model_id, - 'data-test-subj': `custom-inference_${model.model_id}`, - })) || []; + const { isLoading, data: endpoints, resendRequest } = useLoadInferenceEndpoints(); - return inferenceIdOptions; - }, [models]); - - useEffect(() => { - const mergedOptions = { - ...inferenceIdOptionsFromModels.reduce( - (acc, option) => ({ ...acc, [option.label]: option }), - {} - ), - ...defaultInferenceIds.reduce((acc, option) => ({ ...acc, [option.label]: option }), {}), - }; - setOptions(Object.values(mergedOptions)); - }, [inferenceIdOptionsFromModels, defaultInferenceIds]); + const options: EuiSelectableOption[] = useMemo(() => { + const missingDefaultEndpoints = defaultEndpoints.filter( + (endpoint) => !(endpoints || []).find((e) => e.model_id === endpoint.model_id) + ); + const newOptions: EuiSelectableOption[] = [ + ...(endpoints || []), + ...missingDefaultEndpoints, + ].map((endpoint) => ({ + label: endpoint.model_id, + 'data-test-subj': `custom-inference_${endpoint.model_id}`, + checked: value === endpoint.model_id ? 'on' : undefined, + })); + if (value && !newOptions.find((option) => option.label === value)) { + // Sometimes we create a new endpoint but the backend is slow in updating so we need to optimistically update + const newOption: EuiSelectableOption = { + label: value, + checked: 'on', + 'data-test-subj': `custom-inference_${value}`, + }; + return [...newOptions, newOption]; + } + return newOptions; + }, [endpoints, value]); const { showErrorToasts } = useMLModelNotificationToasts(); const onSaveInferenceCallback = useCallback( async (inferenceId: string, taskType: InferenceTaskType, modelConfig: ModelConfig) => { - setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); try { - const isDeployable = - modelConfig.service === Service.elser || modelConfig.service === Service.elasticsearch; - - const newOption: EuiSelectableOption[] = [ - { - label: inferenceId, - checked: 'on', - 'data-test-subj': `custom-inference_${inferenceId}`, - }, - ]; - // uncheck selected endpoint id - uncheckSelectedModelOption(options); - - setOptions([...options, ...newOption]); - - const trainedModelStats = await ml?.mlApi?.trainedModels.getTrainedModelStats(); - const defaultEndpointId = - inferenceServiceTypeElasticsearchModelMap[modelConfig.service] || ''; - const newModelId: InferenceToModelIdMap = {}; - newModelId[inferenceId] = { - trainedModelId: defaultEndpointId, - isDeployable, - isDeployed: - getTrainedModelStats(trainedModelStats)[defaultEndpointId] === DeploymentState.DEPLOYED, - }; - const customInferenceEndpointConfig: CustomInferenceEndpointConfig = { + const trainedModelId = modelConfig.service_settings.model_id || ''; + const customModelConfig = { taskType, modelConfig, }; - setNewInferenceEndpoint(newModelId, customInferenceEndpointConfig); + setIsSaveInferenceLoading(true); + await createInferenceEndpoint(trainedModelId, inferenceId, customModelConfig); + resendRequest(); + setValue(inferenceId); + setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); + setIsSaveInferenceLoading(false); } catch (error) { showErrorToasts(error); + setIsSaveInferenceLoading(false); } }, - [isInferenceFlyoutVisible, ml, setNewInferenceEndpoint, options, showErrorToasts] + [createInferenceEndpoint, setValue, isInferenceFlyoutVisible, showErrorToasts, resendRequest] ); - useEffect(() => { - const subscription = subscribe((updateData) => { - const formData = updateData.data.internal; - const value = formData.main; - onChange(value); - }); - - return subscription.unsubscribe; - }, [subscribe, onChange]); - const selectedOptionLabel = options.find((option) => option.checked)?.label; - useEffect(() => { - setValue(selectedOptionLabel ?? DefaultInferenceModels.elser_model_2); - }, [selectedOptionLabel, setValue]); const [isInferencePopoverVisible, setIsInferencePopoverVisible] = useState(false); const [inferenceEndpointError, setInferenceEndpointError] = useState( undefined @@ -221,7 +185,15 @@ export const SelectInferenceId = ({ async (inferenceId: string) => { const modelsExist = options.some((i) => i.label === inferenceId); if (modelsExist) { - setInferenceEndpointError('Inference Endpoint id already exists'); + setInferenceEndpointError( + i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.defaultLabel', + { + defaultMessage: 'Inference endpoint {inferenceId} already exists', + values: { inferenceId }, + } + ) + ); } else { setInferenceEndpointError(undefined); } @@ -229,139 +201,133 @@ export const SelectInferenceId = ({ [options] ); - const inferencePopover = () => { - return ( - - - {(field) => ( - <> - -

- {field.label} -

-
- - { - setIsInferencePopoverVisible(!isInferencePopoverVisible); - }} - > - {selectedOptionLabel || - i18n.translate( - 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.defaultLabel', - { - defaultMessage: 'No model selected', - } - )} - - - )} -
- - } - isOpen={isInferencePopoverVisible} - panelPaddingSize="m" - closePopover={() => setIsInferencePopoverVisible(!isInferencePopoverVisible)} - > - - option.checked)?.label; + + const inferencePopover = () => ( + + +

+ {config.label} +

+
+ + { - setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); - setInferenceEndpointError(undefined); setIsInferencePopoverVisible(!isInferencePopoverVisible); }} > + {selectedOptionLabel || + i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.alreadyExistsLabel', + { + defaultMessage: 'No inference endpoint selected', + } + )} + + + } + isOpen={isInferencePopoverVisible} + panelPaddingSize="m" + closePopover={() => setIsInferencePopoverVisible(!isInferencePopoverVisible)} + > + + { + setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); + setInferenceEndpointError(undefined); + setIsInferencePopoverVisible(!isInferencePopoverVisible); + }} + > + {i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.addInferenceEndpointButton', + { + defaultMessage: 'Add Inference Endpoint', + } + )} + + + { + const mlTrainedPageUrl = await getMlTrainedModelPageUrl(); + if (typeof mlTrainedPageUrl === 'string') { + application.navigateToUrl(mlTrainedPageUrl); + } + }} + > + {i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.manageInferenceEndpointButton', + { + defaultMessage: 'Manage Inference Endpoints', + } + )} + + + + + +

{i18n.translate( - 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.addInferenceEndpointButton', - { - defaultMessage: 'Add inference Endpoint', - } - )} - - - { - const mlTrainedPageUrl = await getMlTrainedModelPageUrl(); - if (typeof mlTrainedPageUrl === 'string') { - application.navigateToUrl(mlTrainedPageUrl); - } - }} - > - {i18n.translate( - 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.manageInferenceEndpointButton', + 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.selectable.Label', { - defaultMessage: 'Manage Inference Endpoint', + defaultMessage: 'Existing endpoints', } )} - - - - - -

- {i18n.translate( - 'xpack.idxMgmt.mappingsEditor.parameters.inferenceId.popover.selectable.Label', - { - defaultMessage: 'Existing endpoints', - } - )} -

-
- +

+
+ - { - setOptions(newOptions); - setIsInferencePopoverVisible(!isInferencePopoverVisible); - }} - > - {(list, search) => ( - <> - {search} - {list} - - )} - -
-
- ); - }; + ), + }} + options={options} + onChange={(newOptions) => { + setValue(newOptions.find((option) => option.checked)?.label || ''); + }} + > + {(list, search) => ( + <> + {search} + {list} + + )} + + +
+ ); return ( -
+ <> + {inferencePopover()} @@ -378,6 +344,7 @@ export const SelectInferenceId = ({ supportedNlpModels={docLinks.links.enterpriseSearch.supportedNlpModels} nlpImportModel={docLinks.links.ml.nlpImportModel} setInferenceEndpointError={setInferenceEndpointError} + isCreateInferenceApiLoading={isSaveInferenceLoading} /> )} @@ -395,6 +362,6 @@ export const SelectInferenceId = ({ /> - + ); }; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx index 4a3f600753dd4..4f8e6557e334f 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx @@ -14,20 +14,16 @@ import { EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ElasticsearchModelDefaultOptions } from '@kbn/inference_integration_flyout/types'; +import { TrainedModelStat } from '@kbn/ml-plugin/common/types/trained_models'; import { MlPluginStart } from '@kbn/ml-plugin/public'; import classNames from 'classnames'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { EUI_SIZE, TYPE_DEFINITION } from '../../../../constants'; import { fieldSerializer } from '../../../../lib'; -import { useDispatch, useMappingsState } from '../../../../mappings_state_context'; -import { Form, FormDataProvider, UseField, useForm, useFormData } from '../../../../shared_imports'; -import { - CustomInferenceEndpointConfig, - Field, - MainType, - NormalizedFields, -} from '../../../../types'; +import { isSemanticTextField } from '../../../../lib/utils'; +import { useDispatch } from '../../../../mappings_state_context'; +import { Form, FormDataProvider, useForm, useFormData } from '../../../../shared_imports'; +import { Field, MainType, NormalizedFields } from '../../../../types'; import { NameParameter, SubTypeParameter, TypeParameter } from '../../field_parameters'; import { ReferenceFieldSelects } from '../../field_parameters/reference_field_selects'; import { SelectInferenceId } from '../../field_parameters/select_inference_id'; @@ -38,9 +34,11 @@ import { useSemanticText } from './semantic_text/use_semantic_text'; const formWrapper = (props: any) =>
; export interface InferenceToModelIdMap { [key: string]: { - trainedModelId: ElasticsearchModelDefaultOptions | string; + trainedModelId: string; isDeployed: boolean; isDeployable: boolean; + isDownloading: boolean; + modelStats?: TrainedModelStat; // third-party models don't have model stats }; } @@ -48,7 +46,9 @@ export interface SemanticTextInfo { isSemanticTextEnabled?: boolean; indexName?: string; ml?: MlPluginStart; - setErrorsInTrainedModelDeployment: React.Dispatch>; + setErrorsInTrainedModelDeployment: React.Dispatch< + React.SetStateAction> + >; } interface Props { allFields: NormalizedFields['byId']; @@ -73,13 +73,13 @@ export const CreateField = React.memo(function CreateFieldComponent({ isAddingFields, semanticTextInfo, }: Props) { - const { isSemanticTextEnabled, indexName, ml, setErrorsInTrainedModelDeployment } = - semanticTextInfo ?? {}; + const { isSemanticTextEnabled, ml, setErrorsInTrainedModelDeployment } = semanticTextInfo ?? {}; const dispatch = useDispatch(); const { form } = useForm({ serializer: fieldSerializer, options: { stripEmptyFields: false }, + id: 'create-field', }); useFormData({ form }); @@ -93,9 +93,6 @@ export const CreateField = React.memo(function CreateFieldComponent({ return subscription.unsubscribe; }, [dispatch, subscribe]); - const [customInferenceEndpointConfig, setCustomInferenceEndpointConfig] = useState< - CustomInferenceEndpointConfig | undefined - >(undefined); const cancel = () => { if (isAddingFields && onCancelAddingNewFields) { onCancelAddingNewFields(); @@ -104,19 +101,14 @@ export const CreateField = React.memo(function CreateFieldComponent({ } }; - const { - referenceFieldComboValue, - nameValue, - inferenceIdComboValue, - setInferenceValue, - semanticFieldType, - handleSemanticText, - } = useSemanticText({ + const { createInferenceEndpoint, handleSemanticText } = useSemanticText({ form, setErrorsInTrainedModelDeployment, ml, }); + const isSemanticText = form.getFormData().type === 'semantic_text'; + const submitForm = async ( e?: React.FormEvent, exitAfter: boolean = false, @@ -128,11 +120,9 @@ export const CreateField = React.memo(function CreateFieldComponent({ const { isValid, data } = await form.submit(); - if (isValid) { - form.reset(); - - if (data.type === 'semantic_text' && !clickOutside) { - handleSemanticText(data, customInferenceEndpointConfig); + if (isValid && !clickOutside) { + if (isSemanticTextField(data)) { + handleSemanticText(data); } else { dispatch({ type: 'field.add', value: data }); } @@ -140,6 +130,7 @@ export const CreateField = React.memo(function CreateFieldComponent({ if (exitAfter) { cancel(); } + form.reset(); } }; @@ -187,23 +178,19 @@ export const CreateField = React.memo(function CreateFieldComponent({ {/* Field reference_field for semantic_text field type */} - + {isSemanticText && ( + + + + )} {/* Field name */} - + ); - const isAddFieldButtonDisabled = (): boolean => { - if (semanticFieldType) { - return !referenceFieldComboValue || !nameValue || !inferenceIdComboValue; - } - - return false; - }; - const renderFormActions = () => ( {(isCancelable !== false || isAddingFields) && ( @@ -222,7 +209,7 @@ export const CreateField = React.memo(function CreateFieldComponent({ onClick={submitForm} type="submit" data-test-subj="addButton" - isDisabled={isAddFieldButtonDisabled()} + isDisabled={form.getErrors().length > 0} > {isMultiField ? i18n.translate('xpack.idxMgmt.mappingsEditor.createField.addMultiFieldButtonLabel', { @@ -289,11 +276,10 @@ export const CreateField = React.memo(function CreateFieldComponent({ ); }} - {/* Field inference_id for semantic_text field type */} - + + {isSemanticText && ( + + )} {renderFormActions()}
@@ -302,69 +288,3 @@ export const CreateField = React.memo(function CreateFieldComponent({ ); }); - -function ReferenceFieldCombo({ indexName }: { indexName?: string }) { - const [{ type }] = useFormData({ watch: 'type' }); - - if (type === undefined || type[0]?.value !== 'semantic_text') { - return null; - } - - return ( - - - {(field) => } - - - ); -} - -interface InferenceProps { - setValue: (value: string) => void; - setCustomInferenceEndpointConfig: (config: CustomInferenceEndpointConfig) => void; -} - -function InferenceIdCombo({ setValue, setCustomInferenceEndpointConfig }: InferenceProps) { - const { inferenceToModelIdMap } = useMappingsState(); - const dispatch = useDispatch(); - const [{ type }] = useFormData({ watch: 'type' }); - - // update new inferenceEndpoint - const setNewInferenceEndpoint = useCallback( - ( - newInferenceEndpoint: InferenceToModelIdMap, - customInferenceEndpointConfig: CustomInferenceEndpointConfig - ) => { - dispatch({ - type: 'inferenceToModelIdMap.update', - value: { - inferenceToModelIdMap: { - ...inferenceToModelIdMap, - ...newInferenceEndpoint, - }, - }, - }); - setCustomInferenceEndpointConfig(customInferenceEndpointConfig); - }, - [dispatch, inferenceToModelIdMap, setCustomInferenceEndpointConfig] - ); - - if (type === undefined || type[0]?.value !== 'semantic_text') { - return null; - } - - return ( - <> - - - {(field) => ( - - )} - - - ); -} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.test.ts index f9bc12a9022fd..72e007b86f786 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.test.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.test.ts @@ -6,10 +6,37 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { CustomInferenceEndpointConfig, Field } from '../../../../../types'; +import { CustomInferenceEndpointConfig, SemanticTextField } from '../../../../../types'; import { useSemanticText } from './use_semantic_text'; import { act } from 'react-dom/test-utils'; +jest.mock('../../../../../../../../hooks/use_details_page_mappings_model_management', () => ({ + useDetailsPageMappingsModelManagement: () => ({ + fetchInferenceToModelIdMap: () => ({ + e5: { + isDeployed: false, + isDeployable: true, + trainedModelId: '.multilingual-e5-small', + }, + elser_model_2: { + isDeployed: false, + isDeployable: true, + trainedModelId: '.elser_model_2', + }, + openai: { + isDeployed: false, + isDeployable: false, + trainedModelId: '', + }, + my_elser_endpoint: { + isDeployed: false, + isDeployable: true, + trainedModelId: '.elser_model_2', + }, + }), + }), +})); + const mlMock: any = { mlApi: { inferenceModels: { @@ -18,26 +45,30 @@ const mlMock: any = { }, }; -const mockField: Record = { +const mockField: Record = { elser_model_2: { name: 'name', type: 'semantic_text', - inferenceId: 'elser_model_2', + inference_id: 'elser_model_2', + reference_field: 'title', }, e5: { name: 'name', type: 'semantic_text', - inferenceId: 'e5', + inference_id: 'e5', + reference_field: 'title', }, openai: { name: 'name', type: 'semantic_text', - inferenceId: 'openai', + inference_id: 'openai', + reference_field: 'title', }, my_elser_endpoint: { name: 'name', type: 'semantic_text', - inferenceId: 'my_elser_endpoint', + inference_id: 'my_elser_endpoint', + reference_field: 'title', }, }; @@ -90,6 +121,10 @@ jest.mock('../../../../../mappings_state_context', () => ({ trainedModelId: '.elser_model_2', }, }, + fields: { + byId: {}, + }, + mappingViewFields: { byId: {} }, }), useDispatch: () => mockDispatch, })); @@ -128,28 +163,31 @@ describe('useSemanticText', () => { jest.clearAllMocks(); mockForm = { form: { - getFields: jest.fn().mockReturnValue({ - referenceField: { value: 'title' }, - name: { value: 'sem' }, - type: { value: [{ value: 'semantic_text' }] }, - inferenceId: { value: 'e5' }, + getFormData: jest.fn().mockReturnValue({ + referenceField: 'title', + name: 'sem', + type: 'semantic_text', + inferenceId: 'e5', }), + setFieldValue: jest.fn(), }, thirdPartyModel: { - getFields: jest.fn().mockReturnValue({ - referenceField: { value: 'title' }, - name: { value: 'semantic_text_openai_endpoint' }, - type: { value: [{ value: 'semantic_text' }] }, - inferenceId: { value: 'openai' }, + getFormData: jest.fn().mockReturnValue({ + referenceField: 'title', + name: 'semantic_text_openai_endpoint', + type: 'semantic_text', + inferenceId: 'openai', }), + setFieldValue: jest.fn(), }, elasticModelEndpointCreatedfromFlyout: { - getFields: jest.fn().mockReturnValue({ - referenceField: { value: 'title' }, - name: { value: 'semantic_text_elserServiceType_endpoint' }, - type: { value: [{ value: 'semantic_text' }] }, - inferenceId: { value: 'my_elser_endpoint' }, + getFormData: jest.fn().mockReturnValue({ + referenceField: 'title', + name: 'semantic_text_elserServiceType_endpoint', + type: 'semantic_text', + inferenceId: 'my_elser_endpoint', }), + setFieldValue: jest.fn(), }, }; }); @@ -162,11 +200,10 @@ describe('useSemanticText', () => { }) ); await act(async () => { - result.current.setInferenceValue('openai'); result.current.handleSemanticText(mockField.openai, mockConfig.openai); }); expect(mockDispatch).toHaveBeenCalledWith({ - type: 'field.addSemanticText', + type: 'field.add', value: mockField.openai, }); expect(mlMock.mlApi.inferenceModels.createInferenceEndpoint).toHaveBeenCalledWith( @@ -184,12 +221,11 @@ describe('useSemanticText', () => { }) ); await act(async () => { - result.current.setInferenceValue('my_elser_endpoint'); result.current.handleSemanticText(mockField.my_elser_endpoint, mockConfig.elser); }); expect(mockDispatch).toHaveBeenCalledWith({ - type: 'field.addSemanticText', + type: 'field.add', value: mockField.my_elser_endpoint, }); expect(mlMock.mlApi.inferenceModels.createInferenceEndpoint).toHaveBeenCalledWith( @@ -198,20 +234,6 @@ describe('useSemanticText', () => { mockConfig.elser.modelConfig ); }); - it('should populate the values from the form', () => { - const { result } = renderHook(() => - useSemanticText({ - form: mockForm.form, - setErrorsInTrainedModelDeployment: jest.fn(), - ml: mlMock, - }) - ); - - expect(result.current.referenceFieldComboValue).toBe('title'); - expect(result.current.nameValue).toBe('sem'); - expect(result.current.inferenceIdComboValue).toBe('e5'); - expect(result.current.semanticFieldType).toBe('semantic_text'); - }); it('should handle semantic text correctly', async () => { const { result } = renderHook(() => @@ -227,7 +249,7 @@ describe('useSemanticText', () => { }); expect(mockDispatch).toHaveBeenCalledWith({ - type: 'field.addSemanticText', + type: 'field.add', value: mockField.elser_model_2, }); expect(mlMock.mlApi.inferenceModels.createInferenceEndpoint).toHaveBeenCalledWith( @@ -253,12 +275,11 @@ describe('useSemanticText', () => { ); await act(async () => { - result.current.setInferenceValue('e5'); result.current.handleSemanticText(mockField.e5); }); expect(mockDispatch).toHaveBeenCalledWith({ - type: 'field.addSemanticText', + type: 'field.add', value: mockField.e5, }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.ts index 72be2636329a2..2eb4343d0c7a4 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/semantic_text/use_semantic_text.ts @@ -5,23 +5,26 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { useCallback } from 'react'; import { MlPluginStart } from '@kbn/ml-plugin/public'; -import React, { useEffect, useState } from 'react'; -import { ElasticsearchModelDefaultOptions } from '@kbn/inference_integration_flyout/types'; +import React, { useEffect } from 'react'; import { InferenceTaskType } from '@elastic/elasticsearch/lib/api/types'; -import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; +import { ElserModels } from '@kbn/ml-trained-models-utils'; +import { i18n } from '@kbn/i18n'; +import { useDetailsPageMappingsModelManagement } from '../../../../../../../../hooks/use_details_page_mappings_model_management'; import { useDispatch, useMappingsState } from '../../../../../mappings_state_context'; import { FormHook } from '../../../../../shared_imports'; -import { CustomInferenceEndpointConfig, DefaultInferenceModels, Field } from '../../../../../types'; +import { CustomInferenceEndpointConfig, Field, SemanticTextField } from '../../../../../types'; import { useMLModelNotificationToasts } from '../../../../../../../../hooks/use_ml_model_status_toasts'; import { getInferenceEndpoints } from '../../../../../../../services/api'; +import { getFieldByPathName } from '../../../../../lib/utils'; interface UseSemanticTextProps { form: FormHook; ml?: MlPluginStart; - setErrorsInTrainedModelDeployment: React.Dispatch> | undefined; + setErrorsInTrainedModelDeployment?: React.Dispatch< + React.SetStateAction> + >; } interface DefaultInferenceEndpointConfig { taskType: InferenceTaskType; @@ -30,70 +33,52 @@ interface DefaultInferenceEndpointConfig { export function useSemanticText(props: UseSemanticTextProps) { const { form, setErrorsInTrainedModelDeployment, ml } = props; - const { inferenceToModelIdMap } = useMappingsState(); + const { fields, mappingViewFields } = useMappingsState(); + const { fetchInferenceToModelIdMap } = useDetailsPageMappingsModelManagement(); const dispatch = useDispatch(); - const [referenceFieldComboValue, setReferenceFieldComboValue] = useState(); - const [nameValue, setNameValue] = useState(); - const [inferenceIdComboValue, setInferenceIdComboValue] = useState(); - const [semanticFieldType, setSemanticTextFieldType] = useState(); - const [inferenceValue, setInferenceValue] = useState( - DefaultInferenceModels.elser_model_2 - ); - const { showSuccessToasts, showErrorToasts } = useMLModelNotificationToasts(); + const { showSuccessToasts, showErrorToasts, showSuccessfullyDeployedToast } = + useMLModelNotificationToasts(); - const useFieldEffect = ( - semanticTextform: FormHook, - fieldName: string, - setState: React.Dispatch> - ) => { - const fieldValue = semanticTextform.getFields()?.[fieldName]?.value; - useEffect(() => { - if (typeof fieldValue === 'string') { - setState(fieldValue); - } - }, [semanticTextform, fieldValue, setState]); - }; - - useFieldEffect(form, 'referenceField', setReferenceFieldComboValue); - useFieldEffect(form, 'name', setNameValue); - - const fieldTypeValue = form.getFields()?.type?.value; + const fieldTypeValue = form.getFormData()?.type; useEffect(() => { - if (!Array.isArray(fieldTypeValue) || fieldTypeValue.length === 0) { - return; - } - setSemanticTextFieldType( - fieldTypeValue[0]?.value === 'semantic_text' ? fieldTypeValue[0].value : undefined - ); - }, [form, fieldTypeValue]); - - const inferenceId = form.getFields()?.inferenceId?.value; - useEffect(() => { - if (typeof inferenceId === 'string') { - setInferenceIdComboValue(inferenceId); + if (fieldTypeValue === 'semantic_text') { + const allFields = { + byId: { + ...fields.byId, + ...mappingViewFields.byId, + }, + rootLevelFields: [], + aliases: {}, + maxNestedDepth: 0, + }; + const defaultName = getFieldByPathName(allFields, 'semantic_text') ? '' : 'semantic_text'; + const referenceField = + Object.values(allFields.byId) + .find((field) => field.source.type === 'text') + ?.path.join('.') || ''; + if (!form.getFormData().name) { + form.setFieldValue('name', defaultName); + } + if (!form.getFormData().reference_field) { + form.setFieldValue('reference_field', referenceField); + } + if (!form.getFormData().inference_id) { + form.setFieldValue('inference_id', 'elser_model_2'); + } } - }, [form, inferenceId, inferenceToModelIdMap]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fieldTypeValue]); const createInferenceEndpoint = useCallback( async ( - trainedModelId: ElasticsearchModelDefaultOptions | string, - data: Field, + trainedModelId: string, + inferenceId: string, customInferenceEndpointConfig?: CustomInferenceEndpointConfig ) => { - if (data.inferenceId === undefined) { - throw new Error( - i18n.translate('xpack.idxMgmt.mappingsEditor.createField.undefinedInferenceIdError', { - defaultMessage: 'Inference ID is undefined', - }) - ); - } + const isElser = ElserModels.includes(trainedModelId); const defaultInferenceEndpointConfig: DefaultInferenceEndpointConfig = { - service: - trainedModelId === ElasticsearchModelDefaultOptions.elser ? 'elser' : 'elasticsearch', - taskType: - trainedModelId === ElasticsearchModelDefaultOptions.elser - ? 'sparse_embedding' - : 'text_embedding', + service: isElser ? 'elser' : 'elasticsearch', + taskType: isElser ? 'sparse_embedding' : 'text_embedding', }; const modelConfig = customInferenceEndpointConfig @@ -108,65 +93,69 @@ export function useSemanticText(props: UseSemanticTextProps) { }; const taskType: InferenceTaskType = customInferenceEndpointConfig?.taskType ?? defaultInferenceEndpointConfig.taskType; - try { - await ml?.mlApi?.inferenceModels?.createInferenceEndpoint( - data.inferenceId, - taskType, - modelConfig - ); - } catch (error) { - throw error; - } + + await ml?.mlApi?.inferenceModels?.createInferenceEndpoint(inferenceId, taskType, modelConfig); }, [ml?.mlApi?.inferenceModels] ); const handleSemanticText = async ( - data: Field, + data: SemanticTextField, customInferenceEndpointConfig?: CustomInferenceEndpointConfig ) => { - data.inferenceId = inferenceValue; - if (data.inferenceId === undefined) { - return; - } - const inferenceData = inferenceToModelIdMap?.[data.inferenceId]; + const modelIdMap = await fetchInferenceToModelIdMap(); + const inferenceId = data.inference_id; + const inferenceData = modelIdMap?.[inferenceId]; if (!inferenceData) { - return; + throw new Error( + i18n.translate('xpack.idxMgmt.mappingsEditor.semanticText.inferenceError', { + defaultMessage: 'No inference model found for inference ID {inferenceId}', + }) + ); } const { trainedModelId } = inferenceData; - dispatch({ type: 'field.addSemanticText', value: data }); - + dispatch({ type: 'field.add', value: data }); + const inferenceEndpoints = await getInferenceEndpoints(); + const hasInferenceEndpoint = inferenceEndpoints.data?.some( + (inference) => inference.model_id === inferenceId + ); + // if inference endpoint exists already, do not create new inference endpoint + if (hasInferenceEndpoint) { + return; + } try { - // if inference endpoint exists already, do not create inference endpoint - const inferenceModels = await getInferenceEndpoints(); - const inferenceModel: InferenceAPIConfigResponse[] = inferenceModels.data.some( - (e: InferenceAPIConfigResponse) => e.model_id === inferenceValue - ); - if (inferenceModel) { - return; - } // Only show toast if it's an internal Elastic model that hasn't been deployed yet if (trainedModelId && inferenceData.isDeployable && !inferenceData.isDeployed) { showSuccessToasts(trainedModelId); } - - await createInferenceEndpoint(trainedModelId, data, customInferenceEndpointConfig); + await createInferenceEndpoint( + trainedModelId, + data.inference_id, + customInferenceEndpointConfig + ); + if (trainedModelId) { + // clear error because we've succeeded here + setErrorsInTrainedModelDeployment?.((prevItems) => ({ + ...prevItems, + [trainedModelId]: undefined, + })); + } + showSuccessfullyDeployedToast(trainedModelId); } catch (error) { // trainedModelId is empty string when it's a third party model if (trainedModelId) { - setErrorsInTrainedModelDeployment?.((prevItems) => [...prevItems, trainedModelId]); + setErrorsInTrainedModelDeployment?.((prevItems) => ({ + ...prevItems, + [trainedModelId]: error, + })); } showErrorToasts(error); } }; return { - referenceFieldComboValue, - nameValue, - inferenceIdComboValue, - semanticFieldType, + createInferenceEndpoint, handleSemanticText, - setInferenceValue, }; } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx index 790b4560000f7..0be09876b63f2 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx @@ -1041,7 +1041,6 @@ export const PARAMETERS_DEFINITION: { [key in ParameterName]: ParameterDefinitio }, schema: t.number, }, - dims: { fieldConfig: { defaultValue: '', @@ -1070,20 +1069,46 @@ export const PARAMETERS_DEFINITION: { [key in ParameterName]: ParameterDefinitio }, reference_field: { fieldConfig: { + defaultValue: '', label: i18n.translate('xpack.idxMgmt.mappingsEditor.parameters.referenceFieldLabel', { defaultMessage: 'Reference field', }), helpText: i18n.translate('xpack.idxMgmt.mappingsEditor.parameters.referenceFieldHelpText', { defaultMessage: 'Reference field for model inference.', }), + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.validations.referenceFieldIsRequiredErrorMessage', + { + defaultMessage: 'Reference field is required.', + } + ) + ), + }, + ], }, schema: t.string, }, inference_id: { fieldConfig: { + defaultValue: 'elser_model_2', label: i18n.translate('xpack.idxMgmt.mappingsEditor.parameters.inferenceIdLabel', { defaultMessage: 'Select an inference endpoint:', }), + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.validations.inferenceIdIsRequiredErrorMessage', + { + defaultMessage: 'Inference ID is required.', + } + ) + ), + }, + ], }, schema: t.string, }, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts index be00b71b58ea1..3a4f71a8d533b 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts @@ -18,6 +18,7 @@ import { getFieldsFromState, getAllFieldTypesFromState, getFieldsMatchingFilterFromState, + getStateWithCopyToFields, } from './utils'; const fieldsWithnestedFields: NormalizedFields = { @@ -420,6 +421,7 @@ describe('utils', () => { selectedDataTypes: ['Boolean'], }, inferenceToModelIdMap: {}, + mappingViewFields: { byId: {}, rootLevelFields: [], aliases: {}, maxNestedDepth: 0 }, }; test('returns list of matching fields with search term', () => { expect(getFieldsMatchingFilterFromState(sampleState, ['Boolean'])).toEqual({ @@ -442,5 +444,203 @@ describe('utils', () => { }, }); }); + describe('getStateWithCopyToFields', () => { + test('returns state if there is no semantic text field', () => { + const state = { + fields: { + byId: { + '88ebcfdb-19b7-4458-9ea2-9488df54453d': { + id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', + isMultiField: false, + source: { + name: 'title', + type: 'text', + }, + }, + }, + }, + } as any; + expect(getStateWithCopyToFields(state)).toEqual(state); + }); + test('returns state if semantic text field has no reference field', () => { + const state = { + fields: { + byId: { + '88ebcfdb-19b7-4458-9ea2-9488df54453d': { + id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', + isMultiField: false, + source: { + name: 'title', + type: 'semantic_text', + inference_id: 'id', + }, + }, + }, + }, + } as any; + expect(getStateWithCopyToFields(state)).toEqual(state); + }); + test('adds text field with copy to to state if semantic text field has reference field', () => { + const state = { + fields: { + byId: { + '88ebcfdb-19b7-4458-9ea2-9488df54453d': { + id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', + isMultiField: false, + path: ['title'], + source: { + name: 'title', + type: 'semantic_text', + inference_id: 'id', + reference_field: 'new', + }, + }, + 'new-field': { + id: 'new-field', + isMultiField: false, + path: ['new'], + source: { + name: 'new', + type: 'text', + }, + }, + }, + rootLevelFields: ['88ebcfdb-19b7-4458-9ea2-9488df54453d'], + }, + } as any; + const expectedState = { + fields: { + byId: { + '88ebcfdb-19b7-4458-9ea2-9488df54453d': { + id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', + isMultiField: false, + path: ['title'], + source: { + name: 'title', + type: 'semantic_text', + inference_id: 'id', + }, + }, + 'new-field': { + id: 'new-field', + isMultiField: false, + path: ['new'], + source: { + name: 'new', + type: 'text', + copy_to: ['title'], + }, + }, + }, + rootLevelFields: ['88ebcfdb-19b7-4458-9ea2-9488df54453d', 'new-field'], + }, + } as any; + expect(getStateWithCopyToFields(state)).toEqual(expectedState); + }); + test('adds nested text field with copy to to state if semantic text field has reference field', () => { + const state = { + fields: { + byId: { + '88ebcfdb-19b7-4458-9ea2-9488df54453d': { + id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', + isMultiField: false, + path: ['title'], + source: { + name: 'title', + type: 'semantic_text', + inference_id: 'id', + reference_field: 'existing.new', + }, + }, + }, + rootLevelFields: ['88ebcfdb-19b7-4458-9ea2-9488df54453d'], + }, + mappingViewFields: { + byId: { + existing: { + id: 'existing', + isMultiField: false, + path: ['existing'], + source: { + name: 'existing', + type: 'object', + }, + }, + 'new-field': { + id: 'new-field', + parentId: 'existing', + isMultiField: false, + path: ['existing', 'new'], + source: { + name: 'new', + type: 'text', + }, + }, + }, + }, + } as any; + const expectedState = { + fields: { + byId: { + '88ebcfdb-19b7-4458-9ea2-9488df54453d': { + id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', + isMultiField: false, + path: ['title'], + source: { + name: 'title', + type: 'semantic_text', + inference_id: 'id', + }, + }, + existing: { + id: 'existing', + isMultiField: false, + path: ['existing'], + source: { + name: 'existing', + type: 'object', + }, + }, + 'new-field': { + id: 'new-field', + isMultiField: false, + parentId: 'existing', + path: ['existing', 'new'], + source: { + name: 'new', + type: 'text', + copy_to: ['title'], + }, + }, + }, + rootLevelFields: ['88ebcfdb-19b7-4458-9ea2-9488df54453d', 'existing'], + }, + mappingViewFields: { + byId: { + existing: { + id: 'existing', + isMultiField: false, + path: ['existing'], + source: { + name: 'existing', + type: 'object', + }, + }, + 'new-field': { + id: 'new-field', + parentId: 'existing', + isMultiField: false, + path: ['existing', 'new'], + source: { + name: 'new', + type: 'text', + }, + }, + }, + }, + } as any; + expect(getStateWithCopyToFields(state)).toEqual(expectedState); + }); + }); }); }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts index cb95da745a30d..ed966805c0f2e 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts @@ -7,6 +7,7 @@ import { v4 as uuidv4 } from 'uuid'; +import { cloneDeep } from 'lodash'; import { ChildFieldName, ComboBoxOption, @@ -23,6 +24,7 @@ import { ParameterName, RuntimeFields, SubType, + SemanticTextField, } from '../types'; import { @@ -685,3 +687,88 @@ export const getAllFieldTypesFromState = (allFields: Fields): DataType[] => { const fields: DataType[] = []; return getallFieldsIncludingNestedFields(allFields, fields).filter(filterUnique); }; + +export function isSemanticTextField(field: Partial): field is SemanticTextField { + return Boolean(field.inference_id && field.type === 'semantic_text'); +} + +/** + * Returns deep copy of state with `copy_to` added to text fields that are referenced by new semantic text fields + * @param state + * @returns state + */ + +export function getStateWithCopyToFields(state: State): State { + // Make sure we don't accidentally modify existing state + let updatedState = cloneDeep(state); + for (const field of Object.values(updatedState.fields.byId)) { + if (field.source.type === 'semantic_text' && field.source.reference_field) { + // Check fields already added to the list of to-update fields first + // API will not accept reference_field so removing it now + const { reference_field: referenceField, ...source } = field.source; + if (typeof referenceField !== 'string') { + // should never happen + throw new Error('Reference field is not a string'); + } + field.source = source; + const existingTextField = + getFieldByPathName(updatedState.fields, referenceField) || + getFieldByPathName(updatedState.mappingViewFields || { byId: {} }, referenceField); + if (existingTextField) { + // Add copy_to to existing text field's copy_to array + const updatedTextField: NormalizedField = { + ...existingTextField, + source: { + ...existingTextField.source, + copy_to: existingTextField.source.copy_to + ? [ + ...(Array.isArray(existingTextField.source.copy_to) + ? existingTextField.source.copy_to + : [existingTextField.source.copy_to]), + field.path.join('.'), + ] + : [field.path.join('.')], + }, + }; + updatedState = { + ...updatedState, + fields: { + ...updatedState.fields, + byId: { + ...updatedState.fields.byId, + [existingTextField.id]: updatedTextField, + }, + }, + }; + if (existingTextField.parentId) { + let currentField = existingTextField; + let hasParent = true; + while (hasParent) { + if (!currentField.parentId) { + // reached the top of the tree, push current field to root level fields + updatedState.fields.rootLevelFields.push(currentField.id); + hasParent = false; + } else if (updatedState.fields.byId[currentField.parentId]) { + // parent is already in state, don't need to do anything + hasParent = false; + } else { + // parent is not in state yet + updatedState.fields.byId[currentField.parentId] = + updatedState.mappingViewFields.byId[currentField.parentId]; + currentField = updatedState.fields.byId[currentField.parentId]; + } + } + } else { + updatedState.fields.rootLevelFields.push(existingTextField.id); + } + } else { + throw new Error(`Semantic text field ${field.path.join('.')} has invalid reference field`); + } + } + } + return updatedState; +} + +export const getFieldByPathName = (fields: NormalizedFields, name: string) => { + return Object.values(fields.byId).find((field) => field.path.join('.') === name); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state_context.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state_context.tsx index 2b132d377918d..ac19c5395f974 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state_context.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state_context.tsx @@ -60,6 +60,7 @@ export const StateProvider: React.FC<{ children?: React.ReactNode }> = ({ childr selectedDataTypes: [], }, inferenceToModelIdMap: {}, + mappingViewFields: { byId: {}, rootLevelFields: [], aliases: {}, maxNestedDepth: 0 }, }; const [state, dispatch] = useReducer(reducer, initialState); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts index 0e972e9900b9c..626ee0e839a8a 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts @@ -214,6 +214,9 @@ export const reducer = (state: State, action: Action): State => { }, }; } + case 'editor.replaceViewMappings': { + return { ...state, mappingViewFields: action.value.fields }; + } case 'configuration.update': { const nextState = { ...state, @@ -323,28 +326,6 @@ export const reducer = (state: State, action: Action): State => { case 'field.add': { return addFieldToState(action.value, state); } - case 'field.addSemanticText': { - const addTexFieldWithCopyToActionValue: Field = { - name: action.value.referenceField as string, - type: 'text', - copy_to: [action.value.name], - }; - - // Add text field to state with copy_to of semantic_text field - let updatedState = addFieldToState(addTexFieldWithCopyToActionValue, state); - - const addSemanticTextFieldActionValue: Field = { - name: action.value.name, - inference_id: action.value.inferenceId, - type: 'semantic_text', - }; - - // Add semantic_text field to state and reset fieldToAddFieldTo - updatedState = addFieldToState(addSemanticTextFieldActionValue, updatedState); - updatedState.documentFields.fieldToAddFieldTo = undefined; - - return updatedState; - } case 'field.remove': { const field = state.fields.byId[action.value]; const { id, hasChildFields, hasMultiFields } = field; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts index 767d29aaaeda8..7ff156f6817f1 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts @@ -186,9 +186,6 @@ interface FieldBasic { subType?: SubType; properties?: { [key: string]: Omit }; fields?: { [key: string]: Omit }; - referenceField?: string; - inferenceId?: string; - inference_id?: string; // other* exist together as a holder of types that the mappings editor does not yet know about but // enables the user to create mappings with them. @@ -201,6 +198,8 @@ type FieldParams = { export type Field = FieldBasic & Partial; +export type SemanticTextField = Field & { inference_id: string; reference_field: string }; + export interface FieldMeta { childFieldsName: ChildFieldName | undefined; canHaveChildFields: boolean; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/state.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/state.ts index a0e7247c39bc1..f40fe420eb3be 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/state.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/state.ts @@ -108,10 +108,12 @@ export interface State { }; templates: TemplatesFormState; inferenceToModelIdMap?: InferenceToModelIdMap; + mappingViewFields: NormalizedFields; // state of the incoming index mappings, separate from the editor state above } export type Action = | { type: 'editor.replaceMappings'; value: { [key: string]: any } } + | { type: 'editor.replaceViewMappings'; value: { fields: NormalizedFields } } | { type: 'inferenceToModelIdMap.update'; value: { inferenceToModelIdMap?: InferenceToModelIdMap }; @@ -122,7 +124,6 @@ export type Action = | { type: 'templates.save'; value: MappingsTemplates } | { type: 'fieldForm.update'; value: OnFormUpdateArg } | { type: 'field.add'; value: Field } - | { type: 'field.addSemanticText'; value: Field } | { type: 'field.remove'; value: string } | { type: 'field.edit'; value: Field } | { type: 'field.toggleExpand'; value: { fieldId: string; isExpanded?: boolean } } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx index 1bd58bcc8e5f0..5c5e1c6a289fa 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/use_state_listener.tsx @@ -8,6 +8,7 @@ import { useEffect, useMemo } from 'react'; import { EuiSelectableOption } from '@elastic/eui'; +import { cloneDeep } from 'lodash'; import { DocumentFieldsStatus, Field, @@ -182,6 +183,12 @@ export const useMappingsStateListener = ({ onChange, value, status }: Args) => { }, }, }); + dispatch({ + type: 'editor.replaceViewMappings', + value: { + fields: cloneDeep(parsedFieldsDefaultValue), + }, + }); }, [ value, parsedFieldsDefaultValue, diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx index be1fdbaa47315..ac247500e3248 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx @@ -218,6 +218,7 @@ export const TemplateForm = ({ ? serializeAsESLifecycle(wizardData.logistics.lifecycle) : undefined, }, + ignoreMissingComponentTemplates: initialTemplate.ignoreMissingComponentTemplates, }; return cleanupTemplateObject(outputTemplate as TemplateDeserialized); diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx index 88902ff517c35..f1a625dafbbda 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx @@ -29,6 +29,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react'; import { ILicense } from '@kbn/licensing-plugin/public'; import { useUnsavedChangesPrompt } from '@kbn/unsaved-changes-prompt'; +import { + getStateWithCopyToFields, + isSemanticTextField, +} from '../../../../components/mappings_editor/lib/utils'; import { Index } from '../../../../../../common'; import { useDetailsPageMappingsModelManagement } from '../../../../../hooks/use_details_page_mappings_model_management'; import { useAppContext } from '../../../../app_context'; @@ -73,7 +77,6 @@ export const DetailsPageMappingsContent: FunctionComponent<{ http, }, plugins: { ml, licensing }, - url, config, overlays, history, @@ -89,9 +92,9 @@ export const DetailsPageMappingsContent: FunctionComponent<{ }, [licensing]); const { enableSemanticText: isSemanticTextEnabled } = config; - const [errorsInTrainedModelDeployment, setErrorsInTrainedModelDeployment] = useState( - [] - ); + const [errorsInTrainedModelDeployment, setErrorsInTrainedModelDeployment] = useState< + Record + >({}); const hasMLPermissions = capabilities?.ml?.canGetTrainedModels ? true : false; @@ -151,11 +154,10 @@ export const DetailsPageMappingsContent: FunctionComponent<{ [jsonData] ); + const [hasSavedFields, setHasSavedFields] = useState(false); + useMappingsStateListener({ value: parsedDefaultValue, status: 'disabled' }); - const { fetchInferenceToModelIdMap, pendingDeployments } = useDetailsPageMappingsModelManagement( - state.fields, - state.inferenceToModelIdMap - ); + const { fetchInferenceToModelIdMap } = useDetailsPageMappingsModelManagement(); const onCancelAddingNewFields = useCallback(() => { setAddingFields(!isAddingFields); @@ -198,8 +200,6 @@ export const DetailsPageMappingsContent: FunctionComponent<{ }); }, [dispatch, isAddingFields, state]); - const [isModalVisible, setIsModalVisible] = useState(false); - useEffect(() => { if (!isSemanticTextEnabled || !hasMLPermissions) { return; @@ -213,7 +213,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const refreshModal = useCallback(async () => { + const fetchInferenceData = useCallback(async () => { try { if (!isSemanticTextEnabled) { return; @@ -230,35 +230,45 @@ export const DetailsPageMappingsContent: FunctionComponent<{ }, [fetchInferenceToModelIdMap, isSemanticTextEnabled, hasMLPermissions]); const updateMappings = useCallback(async () => { + const hasSemanticText = hasSemanticTextField(state.fields); try { - if (isSemanticTextEnabled && hasMLPermissions) { + if (isSemanticTextEnabled && hasMLPermissions && hasSemanticText) { await fetchInferenceToModelIdMap(); - - if (pendingDeployments.length > 0) { - setIsModalVisible(true); - return; - } } - const denormalizedFields = deNormalize(state.fields); + const fields = hasSemanticText ? getStateWithCopyToFields(state).fields : state.fields; - const { error } = await updateIndexMappings(indexName, denormalizedFields); + const denormalizedFields = deNormalize(fields); - if (!error) { - notificationService.showSuccessToast( - i18n.translate('xpack.idxMgmt.indexDetails.mappings.successfullyUpdatedIndexMappings', { - defaultMessage: 'Updated index mapping', - }) + const inferenceIdsInPendingList = Object.values(deNormalize(fields)) + .filter(isSemanticTextField) + .map((field) => field.inference_id) + .filter( + (inferenceId: string) => + state.inferenceToModelIdMap?.[inferenceId] && + !state.inferenceToModelIdMap?.[inferenceId].isDeployed ); - refetchMapping(); - } else { - setSaveMappingError(error.message); + setHasSavedFields(true); + if (inferenceIdsInPendingList.length === 0) { + const { error } = await updateIndexMappings(indexName, denormalizedFields); + + if (!error) { + notificationService.showSuccessToast( + i18n.translate('xpack.idxMgmt.indexDetails.mappings.successfullyUpdatedIndexMappings', { + defaultMessage: 'Updated index mapping', + }) + ); + refetchMapping(); + setHasSavedFields(false); + } else { + setSaveMappingError(error.message); + } } } catch (exception) { setSaveMappingError(exception.message); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.fields, pendingDeployments]); + }, [state.fields]); const onSearchChange = useCallback( (value: string) => { @@ -494,7 +504,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{ > )} @@ -601,15 +611,17 @@ export const DetailsPageMappingsContent: FunctionComponent<{
- {isModalVisible && isSemanticTextEnabled && ( + {isSemanticTextEnabled && isAddingFields && hasSavedFields && ( )} ); }; + +function hasSemanticTextField(fields: NormalizedFields): boolean { + return Object.values(fields.byId).some((field) => field.source.type === 'semantic_text'); +} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/trained_models_deployment_modal.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/trained_models_deployment_modal.tsx index 8c2bfbbbef96d..4bba408197661 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/trained_models_deployment_modal.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/trained_models_deployment_modal.tsx @@ -8,179 +8,175 @@ import { EuiConfirmModal, useGeneratedHtmlId, EuiHealth } from '@elastic/eui'; import React from 'react'; import { EuiLink } from '@elastic/eui'; -import { useEffect, useState } from 'react'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; +import { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; +import { isSemanticTextField } from '../../../../components/mappings_editor/lib/utils'; +import { deNormalize } from '../../../../components/mappings_editor/lib'; +import { useMLModelNotificationToasts } from '../../../../../hooks/use_ml_model_status_toasts'; +import { useMappingsState } from '../../../../components/mappings_editor/mappings_state_context'; +import { useAppContext } from '../../../../app_context'; -interface SemanticTextProps { - setIsModalVisible: (isVisible: boolean) => void; - refreshModal: () => void; - pendingDeployments: Array; - errorsInTrainedModelDeployment: string[]; - url?: SharePluginStart['url']; +export interface TrainedModelsDeploymentModalProps { + fetchData: () => void; + errorsInTrainedModelDeployment: Record; + setErrorsInTrainedModelDeployment: React.Dispatch< + React.SetStateAction> + >; } const ML_APP_LOCATOR = 'ML_APP_LOCATOR'; const TRAINED_MODELS_MANAGE = 'trained_models'; export function TrainedModelsDeploymentModal({ - setIsModalVisible, - refreshModal, - pendingDeployments = [], - errorsInTrainedModelDeployment = [], - url, -}: SemanticTextProps) { + errorsInTrainedModelDeployment = {}, + fetchData, + setErrorsInTrainedModelDeployment, +}: TrainedModelsDeploymentModalProps) { + const { fields, inferenceToModelIdMap } = useMappingsState(); + const { + plugins: { ml }, + url, + } = useAppContext(); const modalTitleId = useGeneratedHtmlId(); + const [isModalVisible, setIsModalVisible] = useState(false); const closeModal = () => setIsModalVisible(false); const [mlManagementPageUrl, setMlManagementPageUrl] = useState(''); + const { showErrorToasts } = useMLModelNotificationToasts(); useEffect(() => { - setIsModalVisible(pendingDeployments.length > 0); - }, [pendingDeployments, setIsModalVisible]); - - useEffect(() => { - let isCancelled = false; const mlLocator = url?.locators.get(ML_APP_LOCATOR); const generateUrl = async () => { if (mlLocator) { const mlURL = await mlLocator.getUrl({ page: TRAINED_MODELS_MANAGE, }); - if (!isCancelled) { - setMlManagementPageUrl(mlURL); - } + setMlManagementPageUrl(mlURL); } }; generateUrl(); - return () => { - isCancelled = true; - }; }, [url]); - const ErroredDeployments = pendingDeployments.filter( - (deployment) => deployment !== undefined && errorsInTrainedModelDeployment.includes(deployment) - ); + const inferenceIdsInPendingList = useMemo(() => { + return Object.values(deNormalize(fields)) + .filter(isSemanticTextField) + .map((field) => field.inference_id); + }, [fields]); - const PendingModelsDeploymentModal = () => { - const pendingDeploymentsList = pendingDeployments.map((deployment, index) => ( -
  • - - {deployment} - -
  • - )); + const [pendingDeployments, setPendingDeployments] = useState([]); - return ( - -

    - {i18n.translate( - 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.textAboutDeploymentsNotCompleted', - { - defaultMessage: - 'Some fields are referencing models that have not yet completed deployment. Deployment may take a few minutes to complete.', - } - )} -

    -
      {pendingDeploymentsList}
    - - {i18n.translate( - 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.textTrainedModelManagementLink', - { - defaultMessage: 'Go to Trained Model Management', - } - )} - -
    - ); + const startModelAllocation = async (trainedModelId: string) => { + try { + await ml?.mlApi?.trainedModels.startModelAllocation(trainedModelId); + } catch (error) { + setErrorsInTrainedModelDeployment((previousState) => ({ + ...previousState, + [trainedModelId]: error.message, + })); + showErrorToasts(error); + setIsModalVisible(true); + } }; - const ErroredModelsDeploymentModal = () => { - const pendingDeploymentsList = pendingDeployments.map((deployment, index) => ( -
  • - - {deployment} - -
  • - )); + useEffect(() => { + const models = inferenceIdsInPendingList.map( + (inferenceId) => inferenceToModelIdMap?.[inferenceId] + ); + for (const model of models) { + if (model && !model.isDownloading && !model.isDeployed) { + // Sometimes the model gets stuck in a ready to deploy state, so we need to trigger deployment manually + startModelAllocation(model.trainedModelId); + } + } + const pendingModels = models + .map((model) => { + return model?.trainedModelId && !model?.isDeployed ? model?.trainedModelId : ''; + }) + .filter((trainedModelId) => !!trainedModelId); + const uniqueDeployments = pendingModels.filter( + (deployment, index) => pendingModels.indexOf(deployment) === index + ); + setPendingDeployments(uniqueDeployments); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inferenceIdsInPendingList, inferenceToModelIdMap]); + + const erroredDeployments = pendingDeployments.filter( + (deployment) => errorsInTrainedModelDeployment[deployment] + ); - return ( - { + if (erroredDeployments.length > 0 || pendingDeployments.length > 0) { + setIsModalVisible(true); + } + }, [erroredDeployments.length, pendingDeployments.length]); + return isModalVisible ? ( + 0 + ? i18n.translate( + 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.deploymentErrorTitle', + { + defaultMessage: 'Models could not be deployed', + } + ) + : i18n.translate('xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.titleLabel', { + defaultMessage: 'Models still deploying', + }) + } + titleProps={{ id: modalTitleId }} + onCancel={closeModal} + onConfirm={fetchData} + cancelButtonText={i18n.translate( + 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.closeButtonLabel', + { + defaultMessage: 'Close', + } + )} + confirmButtonText={i18n.translate( + 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.refreshButtonLabel', + { + defaultMessage: 'Refresh', + } + )} + defaultFocusedButton="confirm" + data-test-subj="trainedModelsDeploymentModal" + > +

    + {erroredDeployments.length > 0 + ? i18n.translate( + 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.deploymentErrorText', + { + defaultMessage: 'There was an error when trying to deploy the following models.', + } + ) + : i18n.translate( + 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.textAboutDeploymentsNotCompleted', + { + defaultMessage: + 'Some fields are referencing models that have not yet completed deployment. Deployment may take a few minutes to complete.', + } + )} +

    +
      + {(erroredDeployments.length > 0 ? erroredDeployments : pendingDeployments).map( + (deployment) => ( +
    • + + {deployment} + +
    • + ) )} - confirmButtonText={i18n.translate( - 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.deploymentErrorTryAgainButtonLabel', +
    + + {i18n.translate( + 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.textTrainedModelManagementLink', { - defaultMessage: 'Try again', + defaultMessage: 'Go to Trained Model Management', } )} - defaultFocusedButton="confirm" - data-test-subj="trainedModelsErroredDeploymentModal" - > -

    - {i18n.translate( - 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.deploymentErrorText', - { - defaultMessage: 'There was an error when trying to deploy the following models.', - } - )} -

    -
      {pendingDeploymentsList}
    - - {i18n.translate( - 'xpack.idxMgmt.indexDetails.trainedModelsDeploymentModal.deploymentErrorTrainedModelManagementLink', - { - defaultMessage: 'Go to Trained Model Management', - } - )} - -
    - ); - }; - - return ErroredDeployments.length > 0 ? ( - - ) : ( - - ); + +
    + ) : null; } diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts index e071e0bf3c68c..e68335b8e0ba5 100644 --- a/x-pack/plugins/index_management/public/application/services/api.ts +++ b/x-pack/plugins/index_management/public/application/services/api.ts @@ -434,6 +434,7 @@ export function createIndex(indexName: string) { }), }); } + export function updateIndexMappings(indexName: string, newFields: Fields) { return sendRequest({ path: `${API_BASE_PATH}/mapping/${encodeURIComponent(indexName)}`, @@ -443,7 +444,7 @@ export function updateIndexMappings(indexName: string, newFields: Fields) { } export function getInferenceEndpoints() { - return sendRequest({ + return sendRequest({ path: `${API_BASE_PATH}/inference/all`, method: 'get', }); diff --git a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.test.ts b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.test.ts index c3d74c21ec528..6e80bcea5c349 100644 --- a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.test.ts +++ b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.test.ts @@ -6,7 +6,6 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { InferenceToModelIdMap } from '../application/components/mappings_editor/components/document_fields/fields'; import { NormalizedFields } from '../application/components/mappings_editor/types'; import { useDetailsPageMappingsModelManagement } from './use_details_page_mappings_model_management'; @@ -16,13 +15,24 @@ jest.mock('../application/app_context', () => ({ ml: { mlApi: { trainedModels: { + getModelsDownloadStatus: jest.fn().mockResolvedValue({ + '.elser_model_2_linux-x86_64': {}, + }), getTrainedModelStats: jest.fn().mockResolvedValue({ trained_model_stats: [ { - model_id: '.elser_model_2', + model_id: '.elser_model_2-x86_64', deployment_stats: { deployment_id: 'elser_model_2', - model_id: '.elser_model_2', + model_id: '.elser_model_2-x86_64', + state: 'not started', + }, + }, + { + model_id: '.multilingual-e5-small', + deployment_stats: { + deployment_id: 'e5', + model_id: '.multilingual-e5-small', state: 'started', }, }, @@ -55,68 +65,73 @@ jest.mock('../application/services/api', () => ({ jest.mock('../application/components/mappings_editor/mappings_state_context', () => ({ useDispatch: () => mockDispatch, -})); -const mockDispatch = jest.fn(); -const fields = { - byId: { - '88ebcfdb-19b7-4458-9ea2-9488df54453d': { - id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', - isMultiField: false, - source: { - name: 'title', - type: 'text', - copy_to: ['semantic'], + useMappingsState: () => ({ + fields: { + byId: { + '88ebcfdb-19b7-4458-9ea2-9488df54453d': { + id: '88ebcfdb-19b7-4458-9ea2-9488df54453d', + isMultiField: false, + source: { + name: 'title', + type: 'text', + copy_to: ['semantic'], + }, + path: ['title'], + nestedDepth: 0, + childFieldsName: 'fields', + canHaveChildFields: false, + hasChildFields: false, + canHaveMultiFields: true, + hasMultiFields: false, + isExpanded: false, + }, + 'c5d86c82-ea07-4457-b469-3ffd4b96db81': { + id: 'c5d86c82-ea07-4457-b469-3ffd4b96db81', + isMultiField: false, + source: { + name: 'semantic', + inference_id: 'elser_model_2', + type: 'semantic_text', + }, + path: ['semantic'], + nestedDepth: 0, + childFieldsName: 'fields', + canHaveChildFields: false, + hasChildFields: false, + canHaveMultiFields: true, + hasMultiFields: false, + isExpanded: false, + }, }, - path: ['title'], - nestedDepth: 0, - childFieldsName: 'fields', - canHaveChildFields: false, - hasChildFields: false, - canHaveMultiFields: true, - hasMultiFields: false, - isExpanded: false, - }, - 'c5d86c82-ea07-4457-b469-3ffd4b96db81': { - id: 'c5d86c82-ea07-4457-b469-3ffd4b96db81', - isMultiField: false, - source: { - name: 'semantic', - inference_id: 'elser_model_2', - type: 'semantic_text', + aliases: {}, + rootLevelFields: [ + '88ebcfdb-19b7-4458-9ea2-9488df54453d', + 'c5d86c82-ea07-4457-b469-3ffd4b96db81', + ], + maxNestedDepth: 2, + } as NormalizedFields, + inferenceToModelIdMap: { + elser_model_2: { + trainedModelId: '.elser_model_2', + isDeployed: false, + isDeployable: true, + isDownloading: false, + }, + e5: { + trainedModelId: '.multilingual-e5-small', + isDeployed: true, + isDeployable: true, + isDownloading: false, }, - path: ['semantic'], - nestedDepth: 0, - childFieldsName: 'fields', - canHaveChildFields: false, - hasChildFields: false, - canHaveMultiFields: true, - hasMultiFields: false, - isExpanded: false, }, - }, - aliases: {}, - rootLevelFields: ['88ebcfdb-19b7-4458-9ea2-9488df54453d', 'c5d86c82-ea07-4457-b469-3ffd4b96db81'], - maxNestedDepth: 2, -} as NormalizedFields; + }), +})); -const inferenceToModelIdMap = { - elser_model_2: { - trainedModelId: '.elser_model_2', - isDeployed: true, - isDeployable: true, - }, - e5: { - trainedModelId: '.multilingual-e5-small', - isDeployed: true, - isDeployable: true, - }, -} as InferenceToModelIdMap; +const mockDispatch = jest.fn(); describe('useDetailsPageMappingsModelManagement', () => { it('should call the dispatch with correct parameters', async () => { - const { result } = renderHook(() => - useDetailsPageMappingsModelManagement(fields, inferenceToModelIdMap) - ); + const { result } = renderHook(() => useDetailsPageMappingsModelManagement()); await result.current.fetchInferenceToModelIdMap(); @@ -125,14 +140,22 @@ describe('useDetailsPageMappingsModelManagement', () => { value: { inferenceToModelIdMap: { e5: { - isDeployed: false, + isDeployed: true, isDeployable: true, trainedModelId: '.multilingual-e5-small', + isDownloading: false, + modelStats: { + deployment_id: 'e5', + model_id: '.multilingual-e5-small', + state: 'started', + }, }, elser_model_2: { - isDeployed: true, + isDeployed: false, isDeployable: true, - trainedModelId: '.elser_model_2', + trainedModelId: '.elser_model_2_linux-x86_64', + isDownloading: true, + modelStats: undefined, }, }, }, diff --git a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts index 125892bdf6979..2d920a2d18ee0 100644 --- a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts +++ b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts @@ -5,134 +5,130 @@ * 2.0. */ -import { ElasticsearchModelDefaultOptions, Service } from '@kbn/inference_integration_flyout/types'; -import { InferenceStatsResponse } from '@kbn/ml-plugin/public/application/services/ml_api_service/trained_models'; -import type { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; -import { useCallback, useMemo } from 'react'; -import { useAppContext } from '../application/app_context'; +import { Service } from '@kbn/inference_integration_flyout/types'; +import { ModelDownloadState, TrainedModelStat } from '@kbn/ml-plugin/common/types/trained_models'; +import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; +import { + LATEST_ELSER_VERSION, + InferenceServiceSettings, + LocalInferenceServiceSettings, + LATEST_ELSER_MODEL_ID, + LATEST_E5_MODEL_ID, + ElserVersion, +} from '@kbn/ml-trained-models-utils/src/constants/trained_models'; +import { useCallback } from 'react'; +import { AppDependencies, useAppContext } from '../application/app_context'; import { InferenceToModelIdMap } from '../application/components/mappings_editor/components/document_fields/fields'; -import { deNormalize } from '../application/components/mappings_editor/lib'; import { useDispatch } from '../application/components/mappings_editor/mappings_state_context'; -import { - DefaultInferenceModels, - DeploymentState, - NormalizedFields, -} from '../application/components/mappings_editor/types'; +import { DefaultInferenceModels } from '../application/components/mappings_editor/types'; import { getInferenceEndpoints } from '../application/services/api'; -interface InferenceModel { - data: InferenceAPIConfigResponse[]; +function isLocalModel(model: InferenceServiceSettings): model is LocalInferenceServiceSettings { + return Boolean((model as LocalInferenceServiceSettings).service_settings.model_id); } -type DeploymentStatusType = Record; - const getCustomInferenceIdMap = ( - deploymentStatsByModelId: DeploymentStatusType, - models?: InferenceModel -) => { - return models?.data.reduce((inferenceMap, model) => { - const inferenceId = model.model_id; - - const trainedModelId = - 'model_id' in model.service_settings && - (model.service_settings.model_id === ElasticsearchModelDefaultOptions.elser || - model.service_settings.model_id === ElasticsearchModelDefaultOptions.e5) - ? model.service_settings.model_id - : ''; - inferenceMap[inferenceId] = { - trainedModelId, - isDeployable: model.service === Service.elser || model.service === Service.elasticsearch, - isDeployed: deploymentStatsByModelId[trainedModelId] === 'deployed', - }; + models: InferenceAPIConfigResponse[], + modelStatsById: Record, + downloadStates: Record, + elser: string, + e5: string +): InferenceToModelIdMap => { + const inferenceIdMap = models.reduce((inferenceMap, model) => { + const inferenceEntry = isLocalModel(model) + ? { + trainedModelId: model.service_settings.model_id, // third-party models don't have trained model ids + isDeployable: model.service === Service.elser || model.service === Service.elasticsearch, + isDeployed: modelStatsById[model.service_settings.model_id]?.state === 'started', + isDownloading: Boolean(downloadStates[model.service_settings.model_id]), + modelStats: modelStatsById[model.service_settings.model_id], + } + : { + trainedModelId: '', + isDeployable: false, + isDeployed: false, + isDownloading: false, + modelStats: undefined, + }; + inferenceMap[model.model_id] = inferenceEntry; return inferenceMap; }, {}); -}; - -export const getTrainedModelStats = (modelStats?: InferenceStatsResponse): DeploymentStatusType => { - return ( - modelStats?.trained_model_stats.reduce((acc, modelStat) => { - if (modelStat.model_id) { - acc[modelStat.model_id] = - modelStat?.deployment_stats?.state === 'started' - ? DeploymentState.DEPLOYED - : DeploymentState.NOT_DEPLOYED; - } - return acc; - }, {}) || {} - ); -}; - -const getDefaultInferenceIds = (deploymentStatsByModelId: DeploymentStatusType) => { - return { + const defaultInferenceIds = { [DefaultInferenceModels.elser_model_2]: { - trainedModelId: ElasticsearchModelDefaultOptions.elser, + trainedModelId: elser, isDeployable: true, - isDeployed: - deploymentStatsByModelId[ElasticsearchModelDefaultOptions.elser] === - DeploymentState.DEPLOYED, + isDeployed: modelStatsById[elser]?.state === 'started', + isDownloading: Boolean(downloadStates[elser]), + modelStats: modelStatsById[elser], }, [DefaultInferenceModels.e5]: { - trainedModelId: ElasticsearchModelDefaultOptions.e5, + trainedModelId: e5, isDeployable: true, - isDeployed: - deploymentStatsByModelId[ElasticsearchModelDefaultOptions.e5] === DeploymentState.DEPLOYED, + isDeployed: modelStatsById[e5]?.state === 'started', + isDownloading: Boolean(downloadStates[e5]), + modelStats: modelStatsById[e5], }, }; + return { ...defaultInferenceIds, ...inferenceIdMap }; }; -export const useDetailsPageMappingsModelManagement = ( - fields: NormalizedFields, - inferenceToModelIdMap?: InferenceToModelIdMap -) => { +async function getCuratedModelConfig( + ml: AppDependencies['plugins']['ml'] | undefined, + model: string, + version?: ElserVersion +) { + if (ml?.mlApi) { + try { + const result = await ml.mlApi.trainedModels.getCuratedModelConfig( + model, + version ? { version } : undefined + ); + return result.model_id; + } catch (e) { + // pass through and return default models below + } + } + return model === 'elser' ? LATEST_ELSER_MODEL_ID : LATEST_E5_MODEL_ID; +} + +export const useDetailsPageMappingsModelManagement = () => { const { plugins: { ml }, } = useAppContext(); const dispatch = useDispatch(); - const fetchInferenceModelsAndTrainedModelStats = useCallback(async () => { + const fetchInferenceToModelIdMap = useCallback<() => Promise>(async () => { const inferenceModels = await getInferenceEndpoints(); - const trainedModelStats = await ml?.mlApi?.trainedModels.getTrainedModelStats(); - - return { inferenceModels, trainedModelStats }; - }, [ml]); - - const fetchInferenceToModelIdMap = useCallback(async () => { - const { inferenceModels, trainedModelStats } = await fetchInferenceModelsAndTrainedModelStats(); - const deploymentStatsByModelId = getTrainedModelStats(trainedModelStats); - const defaultInferenceIds = getDefaultInferenceIds(deploymentStatsByModelId); - const modelIdMap = getCustomInferenceIdMap(deploymentStatsByModelId, inferenceModels); + const downloadStates = await ml?.mlApi?.trainedModels.getModelsDownloadStatus(); + const elser = await getCuratedModelConfig(ml, 'elser', LATEST_ELSER_VERSION); + const e5 = await getCuratedModelConfig(ml, 'e5'); + const modelStatsById = + trainedModelStats?.trained_model_stats.reduce< + Record + >((acc, { model_id: modelId, deployment_stats: stats }) => { + if (modelId && stats) { + acc[modelId] = stats; + } + return acc; + }, {}) || {}; + const modelIdMap = getCustomInferenceIdMap( + inferenceModels.data || [], + modelStatsById, + downloadStates || {}, + elser, + e5 + ); dispatch({ type: 'inferenceToModelIdMap.update', - value: { inferenceToModelIdMap: { ...defaultInferenceIds, ...modelIdMap } }, + value: { inferenceToModelIdMap: modelIdMap }, }); - }, [dispatch, fetchInferenceModelsAndTrainedModelStats]); - - const inferenceIdsInPendingList = useMemo(() => { - return Object.values(deNormalize(fields)) - .filter((field) => field.type === 'semantic_text' && field.inference_id) - .map((field) => field.inference_id); - }, [fields]); - - const pendingDeployments = useMemo(() => { - return inferenceIdsInPendingList - .map((inferenceId) => { - if (inferenceId === undefined) { - return undefined; - } - const trainedModelId = inferenceToModelIdMap?.[inferenceId]?.trainedModelId ?? ''; - return trainedModelId && !inferenceToModelIdMap?.[inferenceId]?.isDeployed - ? trainedModelId - : undefined; - }) - .filter((trainedModelId) => !!trainedModelId); - }, [inferenceIdsInPendingList, inferenceToModelIdMap]); + return modelIdMap; + }, [dispatch, ml]); return { - pendingDeployments, fetchInferenceToModelIdMap, - fetchInferenceModelsAndTrainedModelStats, }; }; diff --git a/x-pack/plugins/index_management/public/hooks/use_ml_model_status_toasts.ts b/x-pack/plugins/index_management/public/hooks/use_ml_model_status_toasts.ts index cba440186a1d0..7b553f37498d5 100644 --- a/x-pack/plugins/index_management/public/hooks/use_ml_model_status_toasts.ts +++ b/x-pack/plugins/index_management/public/hooks/use_ml_model_status_toasts.ts @@ -27,6 +27,22 @@ export function useMLModelNotificationToasts() { }), }); }; + const showSuccessfullyDeployedToast = (modelName: string) => { + return toasts.addSuccess({ + title: i18n.translate( + 'xpack.idxMgmt.mappingsEditor.createField.modelDeploymentStartedNotification', + { + defaultMessage: 'Model deployment started', + } + ), + text: i18n.translate('xpack.idxMgmt.mappingsEditor.createField.modelDeployedNotification', { + defaultMessage: 'Model {modelName} has been deployed on your machine learning node.', + values: { + modelName, + }, + }), + }); + }; const showErrorToasts = (error: ErrorType) => { const errorObj = extractErrorProperties(error); return toasts.addError(new MLRequestFailure(errorObj, error), { @@ -35,5 +51,5 @@ export function useMLModelNotificationToasts() { }), }); }; - return { showSuccessToasts, showErrorToasts }; + return { showSuccessToasts, showErrorToasts, showSuccessfullyDeployedToast }; } diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index f6ae66d8e52bc..136174dfac165 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -74,7 +74,7 @@ export class IndexMgmtUIPlugin editableIndexSettings: editableIndexSettings ?? 'all', enableMappingsSourceFieldSection: enableMappingsSourceFieldSection ?? true, enableTogglingDataRetention: enableTogglingDataRetention ?? true, - enableSemanticText: enableSemanticText ?? false, + enableSemanticText: enableSemanticText ?? true, }; } diff --git a/x-pack/plugins/index_management/server/config.ts b/x-pack/plugins/index_management/server/config.ts index 8625274e31d2a..f2d1808a44286 100644 --- a/x-pack/plugins/index_management/server/config.ts +++ b/x-pack/plugins/index_management/server/config.ts @@ -36,7 +36,7 @@ const schemaLatest = schema.object( // deprecated as unused after index details page has been implemented enableIndexDetailsPage: schema.boolean({ defaultValue: false }), // deprecate as unused after semantic text is enabled everywhere - enableSemanticText: schema.boolean({ defaultValue: false }), + enableSemanticText: schema.boolean({ defaultValue: true }), }), enableIndexStats: offeringBasedSchema({ // Index stats information is disabled in serverless; refer to the serverless.yml file as the source of truth diff --git a/x-pack/plugins/index_management/test/fixtures/template.ts b/x-pack/plugins/index_management/test/fixtures/template.ts index ca7f625d61bb9..54df4410352b6 100644 --- a/x-pack/plugins/index_management/test/fixtures/template.ts +++ b/x-pack/plugins/index_management/test/fixtures/template.ts @@ -67,6 +67,8 @@ export const getTemplate = ({ indexPatterns = [], template: { settings, aliases, mappings } = {}, dataStream, + composedOf, + ignoreMissingComponentTemplates, hasDatastream = false, isLegacy = false, type = 'default', @@ -98,6 +100,8 @@ export const getTemplate = ({ hasDatastream: dataStream !== undefined ? true : hasDatastream, isLegacy, }, + composedOf, + ignoreMissingComponentTemplates, }; return indexTemplate; diff --git a/x-pack/plugins/integration_assistant/server/graphs/ecs/constants.ts b/x-pack/plugins/integration_assistant/server/graphs/ecs/constants.ts index 607655541ca9d..dcbe1c10356b5 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/ecs/constants.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/ecs/constants.ts @@ -1688,13 +1688,61 @@ export const ECS_TYPES: EcsFields = { }; export const ECS_FIELDS: EcsFields = { + 'as.number': 'Unique number allocated to the autonomous system.', + 'as.organization.name': 'Organization name of the autonomous system.', + 'client.address': 'Client network address.', + 'client.bytes': 'Bytes sent from the client to the server.', + 'client.domain': 'Client domain.', + 'client.geo.city_name': 'City name of the client.', + 'client.geo.continent_name': 'Name of the continent of the client.', + 'client.geo.country_name': 'Country name of the client.', + 'client.geo.location': 'Longitude and latitude of the client.', + 'client.geo.region_name': 'Region name of the client.', + 'client.ip': 'Client IP address.', + 'client.mac': 'MAC address of the client.', + 'client.nat.ip': 'Translated IP client address.', + 'client.nat.port': 'Translated port of client address.', + 'client.packets': 'Packets sent from the client to the server.', + 'client.port': 'Client port.', + 'client.registered_domain': 'The highest registered client domain, stripped of the subdomain.', + 'client.subdomain': 'Subdomain of the client.', + 'client.top_level_domain': 'Top level domain of the client.', + 'cloud.account.id': 'The cloud account or organization id.', + 'cloud.availability_zone': 'Availability zone in which this host is running.', + 'cloud.instance.id': 'Instance ID of the host machine.', + 'cloud.instance.name': 'Instance name of the host machine.', + 'cloud.machine.type': 'Machine type of the host machine.', + 'cloud.project.id': 'The cloud project id.', + 'cloud.project.name': 'The cloud project name.', + 'cloud.provider': 'Name of the cloud provider.', + 'cloud.region': 'Region in which this host is running.', + 'container.id': 'Unique container id.', + 'container.image.name': 'Name of the image the container is built on.', + 'container.image.tag': 'Container image tag.', + 'container.labels': 'Image labels.', + 'container.name': 'Container name.', + 'container.runtime': 'Runtime managing this container.', + 'data_stream.dataset': 'Data stream dataset.', + 'data_stream.namespace': 'Data stream namespace.', + 'data_stream.type': 'Data stream type.', 'destination.address': 'Destination network address.', 'destination.bytes': 'Bytes sent from the destination to the source.', - 'destination.domain': 'The domain name of the destination.', - 'destination.ip': 'IP address of the destination.', + 'destination.domain': 'Destination domain.', + 'destination.geo.city_name': 'City name of the destination.', + 'destination.geo.continent_name': 'Name of the continent of the destination.', + 'destination.geo.country_name': 'Country name of the destination.', + 'destination.geo.location': 'Longitude and latitude of the destination.', + 'destination.geo.region_name': 'Region name of the destination.', + 'destination.ip': 'Destination IP address.', 'destination.mac': 'MAC address of the destination.', + 'destination.nat.ip': 'Translated IP destination address.', + 'destination.nat.port': 'Translated port of destination address.', 'destination.packets': 'Packets sent from the destination to the source.', - 'destination.port': 'Port of the destination.', + 'destination.port': 'Destination port.', + 'destination.registered_domain': + 'The highest registered destination domain, stripped of the subdomain.', + 'destination.subdomain': 'Subdomain of the destination.', + 'destination.top_level_domain': 'Top level domain of the destination.', 'destination.user.domain': 'Name of the directory the user is a member of.', 'destination.user.email': 'User email address.', 'destination.user.full_name': 'Users full name, if available.', @@ -1703,92 +1751,305 @@ export const ECS_FIELDS: EcsFields = { 'destination.user.group.name': 'Name of the group.', 'destination.user.id': 'Unique identifier of the user.', 'destination.user.name': 'Short name or login of the user.', + 'dll.code_signature.status': 'Status of the DLL code signature.', + 'dll.code_signature.subject_name': 'Subject name of the DLL code signer.', + 'dll.code_signature.trusted': 'Flag indicating if the DLL code signature is trusted.', + 'dll.code_signature.valid': 'Flag indicating if the DLL code signature is valid.', + 'dll.hash.md5': 'MD5 hash of the DLL.', + 'dll.hash.sha1': 'SHA1 hash of the DLL.', + 'dll.hash.sha256': 'SHA256 hash of the DLL.', + 'dll.name': 'Name of the DLL.', + 'dll.path': 'Full path to the DLL file.', + 'dll.pe.company': 'PE company name of the DLL.', + 'dll.pe.description': 'PE description of the DLL.', + 'dll.pe.file_version': 'PE file version of the DLL.', + 'dll.pe.imphash': 'PE import hash of the DLL.', + 'dll.pe.original_file_name': 'PE original file name of the DLL.', + 'dll.pe.product': 'PE product name of the DLL.', + 'dll.size': 'Size of the DLL in bytes.', + 'dll.type': 'Type of the DLL.', + 'dns.answers': 'DNS answers.', + 'dns.header_flags': 'DNS header flags.', + 'dns.id': 'DNS message ID.', + 'dns.op_code': 'DNS operation code.', + 'dns.question.class': 'DNS question class.', + 'dns.question.name': 'DNS question name.', + 'dns.question.registered_domain': + 'The highest registered DNS question domain, stripped of the subdomain.', + 'dns.question.subdomain': 'Subdomain of the DNS question.', + 'dns.question.top_level_domain': 'Top level domain of the DNS question.', + 'dns.question.type': 'DNS question type.', + 'dns.resolved_ip': 'Resolved IP addresses from DNS query.', + 'dns.response_code': 'DNS response code.', + 'dns.type': 'DNS query type.', + 'email.attachments': 'Email attachments.', + 'email.bcc.address': 'Email addresses of BCC recipients.', + 'email.bcc.domain': 'Domain of BCC recipients.', + 'email.bcc.local': 'Local part of the BCC recipients.', + 'email.bcc.registered_domain': 'The highest registered BCC domain, stripped of the subdomain.', + 'email.bcc.subdomain': 'Subdomain of the BCC recipients.', + 'email.bcc.top_level_domain': 'Top level domain of the BCC recipients.', + 'email.cc.address': 'Email addresses of CC recipients.', + 'email.cc.domain': 'Domain of CC recipients.', + 'email.cc.local': 'Local part of the CC recipients.', + 'email.cc.registered_domain': 'The highest registered CC domain, stripped of the subdomain.', + 'email.cc.subdomain': 'Subdomain of the CC recipients.', + 'email.cc.top_level_domain': 'Top level domain of the CC recipients.', + 'email.content_type': 'Content type of the email.', + 'email.delivery_timestamp': 'Time the email was delivered.', + 'email.direction': 'Direction of the email.', + 'email.from.address': 'Email address of the sender.', + 'email.from.domain': 'Domain of the sender.', + 'email.from.local': 'Local part of the sender.', + 'email.from.registered_domain': + 'The highest registered sender domain, stripped of the subdomain.', + 'email.from.subdomain': 'Subdomain of the sender.', + 'email.from.top_level_domain': 'Top level domain of the sender.', + 'email.local_id': 'Local identifier of the email.', + 'email.message_id': 'Message ID of the email.', + 'email.origination_timestamp': 'Time the email was originated.', + 'email.reply_to.address': 'Email address of the reply-to.', + 'email.reply_to.domain': 'Domain of the reply-to.', + 'email.reply_to.local': 'Local part of the reply-to.', + 'email.reply_to.registered_domain': + 'The highest registered reply-to domain, stripped of the subdomain.', + 'email.reply_to.subdomain': 'Subdomain of the reply-to.', + 'email.reply_to.top_level_domain': 'Top level domain of the reply-to.', + 'email.sender.address': 'Email address of the sender.', + 'email.sender.domain': 'Domain of the sender.', + 'email.sender.local': 'Local part of the sender.', + 'email.sender.registered_domain': + 'The highest registered sender domain, stripped of the subdomain.', + 'email.sender.subdomain': 'Subdomain of the sender.', + 'email.sender.top_level_domain': 'Top level domain of the sender.', + 'email.size': 'Size of the email in bytes.', + 'email.subject': 'Subject of the email.', + 'email.to.address': 'Email addresses of recipients.', + 'email.to.domain': 'Domain of recipients.', + 'email.to.local': 'Local part of the recipients.', + 'email.to.registered_domain': + 'The highest registered recipient domain, stripped of the subdomain.', + 'email.to.subdomain': 'Subdomain of the recipients.', + 'email.to.top_level_domain': 'Top level domain of the recipients.', + 'error.code': 'Error code describing the error.', + 'error.id': 'Unique identifier for the error.', + 'error.stack_trace': 'Error stack trace.', + 'error.type': 'Error type.', 'event.action': 'The action captured by the event.', - 'event.created': 'Time when the event was first read by an agent or by your pipeline.', 'event.code': 'Identification code for this event.', 'event.duration': 'Duration of the event in nanoseconds.', - 'event.end': - 'event.end contains the date when the event ended or when the activity was last observed.', + 'event.end': 'Date when the event ended.', + 'event.hash': 'Hash of the event.', 'event.id': 'Unique ID to describe the event.', + 'event.module': 'Name of the module this data is coming from.', + 'event.outcome': 'The outcome of the event.', + 'event.provider': 'Source of the event.', + 'event.reason': 'Reason why this event happened.', + 'event.reference': 'Reference URL describing this event.', + 'event.result': 'The result of the event.', + 'event.risk_score': 'Risk score or priority of the event.', + 'event.risk_score_norm': 'Normalized risk score or priority of the event.', + 'event.sequence': 'Sequence number of the event.', 'event.severity': 'Numeric severity of the event.', - 'file.directory': 'Directory where the file is located.', - 'file.extension': 'File extension, excluding the leading dot.', - 'file.gid': 'Primary group ID (GID) of the file.', - 'file.group': 'Primary group name of the file.', - 'file.hash.md5': 'MD5 hash.', - 'file.hash.sha1': 'SHA1 hash.', - 'file.hash.sha256': 'SHA256 hash.', + 'event.start': 'Date when the event started.', + 'event.timezone': 'Timezone in which the event was captured.', + 'faas.coldstart': 'Indicator of a cold start invocation.', + 'faas.execution': 'Execution identifier.', + 'faas.id': 'Function ID.', + 'faas.name': 'Function name.', + 'faas.version': 'Function version.', + 'file.accessed': 'Last time the file was accessed.', + 'file.attributes': 'File attributes.', + 'file.created': 'Date/time when the file was created.', + 'file.ctime': 'Last time the file attributes or metadata changed.', + 'file.device': 'Device that is the source of the file.', + 'file.directory': 'Directory containing the file.', + 'file.drive_letter': 'Drive letter where the file is located.', + 'file.extension': 'File extension.', + 'file.gid': 'GID or group ID of the file.', + 'file.group': 'Group that owns the file.', + 'file.hash.md5': 'MD5 hash of the file.', + 'file.hash.sha1': 'SHA1 hash of the file.', + 'file.hash.sha256': 'SHA256 hash of the file.', 'file.inode': 'Inode representing the file in the filesystem.', - 'file.name': 'Name of the file including the extension, without the directory.', - 'file.path': 'Full path to the file, including the file name.', + 'file.mime_type': 'File MIME type.', + 'file.mode': 'File mode.', + 'file.mtime': 'Last time the file content was modified.', + 'file.name': 'File name.', + 'file.owner': 'File owner.', + 'file.path': 'File path.', + 'file.puid': 'PUID or platform user ID of the file.', 'file.size': 'File size in bytes.', - 'file.uid': 'The user ID (UID) or security identifier (SID) of the file owner.', - 'group.domain': 'Name of the directory the group is a member of.', + 'file.target_path': 'Target path for symbolic links.', + 'file.type': 'File type (file, dir, symlink, etc).', + 'file.uid': 'UID or user ID of the file.', + 'group.domain': 'Domain of the group.', 'group.id': 'Unique identifier for the group on the system/platform.', 'group.name': 'Name of the group.', + 'host.architecture': 'Operating system architecture.', + 'host.boot.id': 'Unique host boot identifier.', + 'host.cpu.usage': 'Percent CPU usage.', + 'host.disk.read.bytes': 'Total bytes read.', + 'host.disk.write.bytes': 'Total bytes written.', + 'host.geo.city_name': 'City name of the host.', + 'host.geo.continent_name': 'Name of the continent of the host.', + 'host.geo.country_name': 'Country name of the host.', + 'host.geo.location': 'Longitude and latitude of the host.', + 'host.geo.region_name': 'Region name of the host.', + 'host.hostname': 'Hostname of the host.', + 'host.id': 'Unique host id.', + 'host.ip': 'IP address of the host.', + 'host.mac': 'MAC address of the host.', + 'host.name': 'Name of the host.', + 'host.os.build': 'OS build information.', + 'host.os.family': 'OS family (e.g., redhat, debian, freebsd, windows).', + 'host.os.full': 'Operating system name, including the version or code name.', + 'host.os.kernel': 'Operating system kernel version.', + 'host.os.name': 'Operating system name.', + 'host.os.platform': 'Operating system platform (e.g., centos, ubuntu, windows).', + 'host.os.type': 'Operating system type (linux, macos, unix, windows).', + 'host.os.version': 'Operating system version.', + 'http.request.body.bytes': 'Size in bytes of the request body.', + 'http.request.body.content': 'The full HTTP request body.', + 'http.request.headers': 'HTTP request headers.', 'http.request.method': 'HTTP request method.', + 'http.request.mime_type': 'MIME type of the HTTP request.', + 'http.request.referrer': 'Referrer for this HTTP request.', + 'http.response.body.bytes': 'Size in bytes of the response body.', + 'http.response.body.content': 'The full HTTP response body.', + 'http.response.headers': 'HTTP response headers.', + 'http.response.mime_type': 'MIME type of the HTTP response.', 'http.response.status_code': 'HTTP response status code.', 'http.version': 'HTTP version.', - 'network.application': 'Application level protocol name.', - 'network.bytes': 'Total bytes transferred in both directions.', - 'network.direction': 'Direction of the network traffic.', - 'network.packets': 'Total packets transferred in both directions.', - 'network.protocol': 'Application protocol name.', - 'network.transport': 'Protocol Name corresponding to the field `iana_number`.', - 'network.type': 'In the OSI Model this would be the Network Layer. ipv4, ipv6, ipsec, pim, etc', + 'interface.alias': 'Interface alias name.', + 'interface.id': 'Unique identifier for the interface.', + 'interface.name': 'Interface name.', + 'interface.type': 'Interface type.', + 'log.file.path': 'Path to the log file.', + 'log.level': 'Log level of the log event.', + 'log.logger': 'Name of the logger.', + 'log.origin.file.line': + 'Line number of the file containing the source code which originated the log.', + 'log.origin.file.name': 'Name of the file containing the source code which originated the log.', + 'log.origin.function': 'Name of the function which originated the log.', + 'log.original': 'The original log message before any modification.', + 'log.syslog.facility.code': 'Syslog facility code.', + 'log.syslog.facility.name': 'Syslog facility name.', + 'log.syslog.severity.code': 'Syslog severity code.', + 'log.syslog.severity.name': 'Syslog severity name.', + 'metricset.name': 'Name of the metricset.', + 'network.application': 'Network application.', + 'network.bytes': 'Total bytes transferred in the network event.', + 'network.community_id': 'A hash of source and destination IPs, ports, and protocol.', + 'network.direction': 'Network traffic direction (inbound, outbound).', + 'network.forwarded_ip': + 'The field is used to store the original source IP when a proxy or load balancer is in place.', + 'network.iana_number': 'IANA Protocol Number.', + 'network.name': 'Name given by operators to sections of their network.', + 'network.packets': 'Total packets transferred in the network event.', + 'network.protocol': 'L7 Network protocol name.', + 'network.transport': 'L4 Network transport protocol.', + 'network.type': 'Type of the network.', 'organization.id': 'Unique identifier for the organization.', - 'organization.name': 'Organization name.', + 'organization.name': 'Name of the organization.', + 'package.architecture': 'Package architecture.', + 'package.checksum': 'Checksum of the package.', + 'package.description': 'Package description.', + 'package.install_scope': 'Scope of the package installation.', + 'package.license': 'Package license.', + 'package.name': 'Package name.', + 'package.path': 'Package path.', + 'package.reference': 'Package reference.', + 'package.size': 'Package size.', + 'package.version': 'Package version.', 'process.args': 'Array of process arguments.', 'process.args_count': 'Length of the process.args array.', + 'process.code_signature.status': 'Status of the process code signature.', + 'process.code_signature.subject_name': 'Subject name of the process code signer.', + 'process.code_signature.trusted': 'Flag indicating if the process code signature is trusted.', + 'process.code_signature.valid': 'Flag indicating if the process code signature is valid.', 'process.command_line': 'Full command line that started the process.', 'process.end': 'The time the process ended.', 'process.executable': 'Absolute path to the process executable.', - 'process.hash.md5': 'MD5 hash.', - 'process.hash.sha1': 'SHA1 hash.', - 'process.hash.sha256': 'SHA256 hash.', + 'process.exit_code': 'Exit code of the process.', + 'process.hash.md5': 'MD5 hash of the process executable.', + 'process.hash.sha1': 'SHA1 hash of the process executable.', + 'process.hash.sha256': 'SHA256 hash of the process executable.', 'process.name': 'Process name.', - 'process.parent.args': 'Array of process arguments.', + 'process.parent.args': 'Array of parent process arguments.', 'process.parent.args_count': 'Length of the process.args array.', - 'process.parent.command_line': 'Full command line that started the process.', - 'process.parent.end': 'The time the process ended.', - 'process.parent.executable': 'Absolute path to the process executable.', + 'process.parent.code_signature.status': 'Status of the parent process code signature.', + 'process.parent.code_signature.subject_name': 'Subject name of the parent process code signer.', + 'process.parent.code_signature.trusted': + 'Flag indicating if the parent process code signature is trusted.', + 'process.parent.code_signature.valid': + 'Flag indicating if the parent process code signature is valid.', + 'process.parent.command_line': 'Full command line that started the parent process.', + 'process.parent.end': 'The time the parent process ended.', + 'process.parent.executable': 'Absolute path to the parent process executable.', + 'process.parent.exit_code': 'Exit code of the parent process.', 'process.parent.group.id': 'Unique identifier for the group on the system/platform.', 'process.parent.group.name': 'Name of the group.', - 'process.parent.hash.md5': 'MD5 hash.', - 'process.parent.hash.sha1': 'SHA1 hash.', - 'process.parent.hash.sha256': 'SHA256 hash.', - 'process.parent.name': 'Process name.', + 'process.parent.hash.md5': 'MD5 hash of the parent process executable.', + 'process.parent.hash.sha1': 'SHA1 hash of the parent process executable.', + 'process.parent.hash.sha256': 'SHA256 hash of the parent process executable.', + 'process.parent.name': 'Parent process name.', 'process.parent.pgid': 'Deprecated identifier of the group of processes the process belongs to.', - 'process.parent.pid': 'Process id.', - 'process.parent.start': 'The time the process started.', - 'process.parent.thread.id': 'Thread ID.', - 'process.parent.thread.name': 'Thread name.', - 'process.parent.user.id': 'Unique identifier of the user.', + 'process.parent.pid': 'Parent process ID.', + 'process.parent.start': 'The date/time when the parent process started.', + 'process.parent.working_directory': 'The working directory of the parent process.', 'process.parent.user.name': 'Short name or login of the user.', 'process.pgid': 'Deprecated identifier of the group of processes the process belongs to.', - 'process.pid': 'Process id.', - 'process.start': 'The time the process started.', + 'process.pid': 'Process ID.', + 'process.start': 'The date/time when the process started.', 'process.thread.id': 'Thread ID.', 'process.thread.name': 'Thread name.', - 'process.user.id': 'Unique identifier of the user.', - 'process.user.name': 'Short name or login of the user.', - 'rule.author': 'Rule author', - 'rule.category': 'Rule category', - 'rule.description': 'Rule description', - 'rule.id': 'Rule ID', + 'process.title': 'Process title.', + 'process.working_directory': 'The working directory of the process.', + 'rule.author': 'Author of the rule.', + 'rule.category': 'Rule category.', + 'rule.description': 'Rule description.', + 'rule.id': 'Unique rule ID.', 'rule.license': 'Rule license', - 'rule.name': 'Rule name', + 'rule.name': 'Name of the rule.', 'rule.reference': 'Rule reference URL', - 'rule.ruleset': 'Rule ruleset', - 'rule.uuid': 'Rule UUID', 'rule.version': 'Rule version', + 'rule.ruleset': 'Rule set to which the rule belongs.', + 'rule.uuid': 'Rule UUID.', + 'server.address': 'Server network address.', + 'server.bytes': 'Bytes sent from the server to the client.', + 'server.domain': 'Server domain.', + 'server.geo.city_name': 'City name of the server.', + 'server.geo.continent_name': 'Name of the continent of the server.', + 'server.geo.country_name': 'Country name of the server.', + 'server.geo.location': 'Longitude and latitude of the server.', + 'server.geo.region_name': 'Region name of the server.', + 'server.ip': 'Server IP address.', + 'server.mac': 'MAC address of the server.', + 'server.nat.ip': 'Translated IP server address.', + 'server.nat.port': 'Translated port of server address.', + 'server.packets': 'Packets sent from the server to the client.', + 'server.port': 'Server port.', + 'server.registered_domain': 'The highest registered server domain, stripped of the subdomain.', + 'server.subdomain': 'Subdomain of the server.', + 'server.top_level_domain': 'Top level domain of the server.', 'source.address': 'Source network address.', 'source.bytes': 'Bytes sent from the source to the destination.', - 'source.domain': 'The domain name of the source.', - 'source.ip': 'IP address of the source.', + 'source.domain': 'Source domain.', + 'source.geo.city_name': 'City name of the source.', + 'source.geo.continent_name': 'Name of the continent of the source.', + 'source.geo.country_name': 'Country name of the source.', + 'source.geo.location': 'Longitude and latitude of the source.', + 'source.geo.region_name': 'Region name of the source.', + 'source.ip': 'Source IP address.', 'source.mac': 'MAC address of the source.', + 'source.nat.ip': 'Translated IP source address.', + 'source.nat.port': 'Translated port of source address.', 'source.packets': 'Packets sent from the source to the destination.', - 'source.port': 'Port of the source.', + 'source.port': 'Source port.', + 'source.registered_domain': 'The highest registered source domain, stripped of the subdomain.', + 'source.subdomain': 'Subdomain of the source.', + 'source.top_level_domain': 'Top level domain of the source.', 'source.user.domain': 'Name of the directory the user is a member of.', 'source.user.email': 'User email address.', 'source.user.full_name': 'Users full name, if available.', @@ -1798,33 +2059,92 @@ export const ECS_FIELDS: EcsFields = { 'source.user.id': 'Unique identifier of the user.', 'source.user.name': 'Short name or login of the user.', 'source.user.roles': 'Array of user roles at the time of the event.', + 'threat.framework': 'Name of the threat framework used.', + 'threat.tactic.id': 'The ID of the tactic.', + 'threat.tactic.name': 'Name of the tactic.', + 'threat.tactic.reference': 'Reference URL of the tactic.', + 'threat.technique.id': 'The ID of the technique.', + 'threat.technique.name': 'Name of the technique.', + 'threat.technique.reference': 'Reference URL of the technique.', + 'threat.technique.subtechnique.id': 'The ID of the subtechnique.', + 'threat.technique.subtechnique.name': 'Name of the subtechnique.', + 'threat.technique.subtechnique.reference': 'Reference URL of the subtechnique.', 'tls.server.x509.alternative_names': 'List of subject alternative names (SAN).', 'tls.server.x509.issuer.common_name': 'List of common name (CN) of issuing certificate authority.', - 'threat.framework': 'Threat classification framework.', - 'threat.tactic.id': 'Threat tactic id.', - 'threat.tactic.name': 'Threat tactic.', - 'threat.technique.id': 'Threat technique id.', - 'threat.technique.name': 'Threat technique name.', - 'url.domain': 'Domain of the url.', - 'url.extension': 'File extension from the request url, excluding the leading dot.', - 'url.fragment': 'Portion of the url after the `#`.', - 'url.full': 'Full unparsed URL.', - 'url.original': 'Unmodified original url as seen in the event source.', - 'url.path': 'Path of the request, such as "/search".', - 'url.port': 'Port of the request, such as 443.', - 'url.query': 'Query string of the request.', - 'url.scheme': 'Scheme of the url.', - 'user.domain': 'Name of the directory the user is a member of.', - 'user.email': 'User email address.', - 'user.full_name': 'Users full name, if available.', - 'user.group.domain': 'Name of the directory the group is a member of.', - 'user.group.id': 'Unique identifier for the group on the system/platform.', - 'user.group.name': 'Name of the group.', + 'trace.id': 'Unique identifier for a trace.', + 'transaction.id': 'Unique identifier for a transaction.', + 'url.domain': 'Domain of the URL.', + 'url.extension': 'File extension from the URL.', + 'url.fragment': 'Fragment identifier of the URL.', + 'url.full': 'Full URL.', + 'url.original': 'Original URL.', + 'url.password': 'Password from the URL.', + 'url.path': 'Path of the URL.', + 'url.port': 'Port of the URL.', + 'url.query': 'Query string of the URL.', + 'url.registered_domain': 'The highest registered domain, stripped of the subdomain.', + 'url.scheme': 'Scheme of the URL.', + 'url.subdomain': 'Subdomain of the URL.', + 'url.top_level_domain': 'Top level domain of the URL.', + 'url.username': 'Username from the URL.', + 'user.changes.domain': 'Domain of the target user.', + 'user.changes.email': "Target user's email address.", + 'user.changes.full_name': "Target user's full name.", + 'user.changes.group.id': "Unique identifier for the target user's group.", + 'user.changes.group.name': "Target user's group name.", + 'user.changes.id': 'Unique identifier of the target user.', + 'user.changes.name': "Target user's name.", + 'user.domain': 'Domain of the user.', + 'user.effective.domain': 'Domain of the effective user.', + 'user.effective.email': "Effective user's email address.", + 'user.effective.full_name': "Effective user's full name.", + 'user.effective.group.id': "Unique identifier for the effective user's group.", + 'user.effective.group.name': "Effective user's group name.", + 'user.effective.id': 'Unique identifier of the effective user.', + 'user.effective.name': "Effective user's name.", + 'user.email': "User's email address.", + 'user.full_name': "User's full name.", + 'user.group.domain': "Domain of the user's group.", + 'user.group.id': "Unique identifier for the user's group.", + 'user.group.name': "User's group name.", + 'user.hash': 'User hash.', 'user.id': 'Unique identifier of the user.', - 'user.name': 'Short name or login of the user.', - 'user.roles': 'Array of user roles at the time of the event.', - 'user_agent.original': 'Unparsed user_agent string.', + 'user.name': "User's name.", + 'user.target.domain': 'Domain of the target user.', + 'user.target.email': "Target user's email address.", + 'user.target.full_name': "Target user's full name.", + 'user.target.group.id': "Unique identifier for the target user's group.", + 'user.target.group.name': "Target user's group name.", + 'user.target.id': 'Unique identifier of the target user.', + 'user.target.name': "Target user's name.", + 'user_agent.device.name': 'Device name from the user agent string.', + 'user_agent.name': 'Name of the user agent.', + 'user_agent.original': 'Original user agent string.', + 'user_agent.os.family': 'OS family from the user agent string.', + 'user_agent.os.full': 'Full OS information from the user agent string.', + 'user_agent.os.name': 'OS name from the user agent string.', + 'user_agent.os.version': 'OS version from the user agent string.', + 'user_agent.version': 'Version of the user agent.', + 'vlan.id': 'VLAN ID.', + 'vlan.name': 'VLAN name.', + 'vulnerability.category': 'Vulnerability category.', + 'vulnerability.classification': 'Vulnerability classification.', + 'vulnerability.description': 'Vulnerability description.', + 'vulnerability.enumeration': 'Vulnerability enumeration.', + 'vulnerability.id': 'Unique vulnerability identifier.', + 'vulnerability.reference': 'Vulnerability reference URL.', + 'vulnerability.report_id': 'The report or scan identification number.', + 'vulnerability.scanner.vendor': 'Name of the vulnerability scanner vendor.', + 'vulnerability.score.base': + 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe. Base scores cover an assessment for exploitability metrics (attack vector, complexity, privileges, and user interaction), impact metrics (confidentiality, integrity, and availability), and scope..', + 'vulnerability.score.environmental': + 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe. Environmental scores cover an assessment for any modified Base metrics, confidentiality, integrity, and availability requirements.', + 'vulnerability.score.temporal': + 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe. Temporal scores cover an assessment for code maturity, remediation level, and confidence.', + 'vulnerability.score.version': + 'The National Vulnerability Database (NVD) provides qualitative severity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score ranges in addition to the severity ratings for CVSS v3.0 as they are defined in the CVSS v3.0 specification.', + 'vulnerability.severity': 'Vulnerability severity.', }; export const ECS_EXAMPLE_ANSWER = { diff --git a/x-pack/plugins/lens/kibana.jsonc b/x-pack/plugins/lens/kibana.jsonc index 367260bae0f00..10eb3721414fe 100644 --- a/x-pack/plugins/lens/kibana.jsonc +++ b/x-pack/plugins/lens/kibana.jsonc @@ -56,7 +56,7 @@ "embeddable", "fieldFormats", "charts", - "textBasedLanguages", + "esql", ], "extraPublicDirs": [ "common/constants" diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 5c163df2c0715..98233c7ebffef 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -26,7 +26,7 @@ import { getLanguageDisplayName, } from '@kbn/es-query'; import type { AggregateQuery, Query } from '@kbn/es-query'; -import { TextBasedLangEditor } from '@kbn/text-based-languages/public'; +import { TextBasedLangEditor } from '@kbn/esql/public'; import { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common'; import { buildExpression } from '../../../editor_frame_service/editor_frame/expression_helpers'; import { MAX_NUM_OF_COLUMNS } from '../../../datasources/text_based/utils'; diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_popover.scss b/x-pack/plugins/lens/public/shared_components/toolbar_popover.scss index c06f13dfc2eb1..823859866eb2a 100644 --- a/x-pack/plugins/lens/public/shared_components/toolbar_popover.scss +++ b/x-pack/plugins/lens/public/shared_components/toolbar_popover.scss @@ -1,3 +1,3 @@ .lnsVisToolbar__popover { - width: 404px; + width: 410px; } diff --git a/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx b/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx index 327bdef6c5bc4..9d5e081ba351d 100644 --- a/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/visualizations/heatmap/toolbar_component.tsx @@ -60,7 +60,7 @@ export const HeatmapToolbar = memo( +>; + +/** + * Defaults for select optional Metric vis state options + */ +export const metricStateDefaults: Required< + Pick< + MetricVisualizationStateOptionals, + 'titlesTextAlign' | 'valuesTextAlign' | 'iconAlign' | 'valueFontMode' + > +> = { + titlesTextAlign: 'left', + valuesTextAlign: 'right', + iconAlign: 'left', + valueFontMode: 'default', +}; diff --git a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx index 429a681027e63..a239b12deb5be 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx @@ -13,7 +13,7 @@ import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { euiLightVars } from '@kbn/ui-theme'; import { CustomPaletteParams, PaletteOutput, PaletteRegistry } from '@kbn/coloring'; import { VisualizationDimensionEditorProps } from '../../types'; -import { MetricVisualizationState } from './visualization'; +import { MetricVisualizationState } from './types'; import { DimensionEditor, DimensionEditorAdditionalSection, @@ -59,6 +59,10 @@ describe('dimension editor', () => { palette, icon: 'tag', showBar: true, + titlesTextAlign: 'left', + valuesTextAlign: 'right', + iconAlign: 'left', + valueFontMode: 'default', trendlineLayerId: 'second', trendlineLayerType: 'metricTrendline', trendlineMetricAccessor: 'trendline-metric-col-id', diff --git a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx index 757700fa80938..f040c6dc86fa4 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx @@ -34,14 +34,10 @@ import { isNumericFieldForDatatable } from '../../../common/expressions/datatabl import { applyPaletteParams, PalettePanelContainer } from '../../shared_components'; import type { VisualizationDimensionEditorProps } from '../../types'; import { defaultNumberPaletteParams, defaultPercentagePaletteParams } from './palette_config'; -import { - DEFAULT_MAX_COLUMNS, - getDefaultColor, - MetricVisualizationState, - showingBar, -} from './visualization'; +import { DEFAULT_MAX_COLUMNS, getDefaultColor, showingBar } from './visualization'; import { CollapseSetting } from '../../shared_components/collapse_setting'; import { iconsSet } from './icon_set'; +import { MetricVisualizationState } from './types'; export type SupportingVisType = 'none' | 'bar' | 'trendline'; diff --git a/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts index 22d701cee570b..fff365e0e89dd 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.test.ts @@ -7,7 +7,7 @@ import { getSuggestions } from './suggestions'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import { MetricVisualizationState } from './visualization'; +import { MetricVisualizationState } from './types'; import { IconChartMetric } from '@kbn/chart-icons'; const metricColumn = { diff --git a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts index a4077b4aca450..b405ea646d980 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/suggestions.ts @@ -8,7 +8,8 @@ import { IconChartMetric } from '@kbn/chart-icons'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import type { TableSuggestion, Visualization } from '../../types'; -import { metricLabel, MetricVisualizationState, supportedDataTypes } from './visualization'; +import { MetricVisualizationState } from './types'; +import { metricLabel, supportedDataTypes } from './visualization'; const MAX_BUCKETED_COLUMNS = 1; const MAX_METRIC_COLUMNS = 2; // primary and secondary metric diff --git a/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts b/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts index c37ae2d57544e..d0ff261653e1f 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts @@ -17,7 +17,9 @@ import { CollapseArgs, CollapseFunction } from '../../../common/expressions'; import { CollapseExpressionFunction } from '../../../common/expressions/collapse/types'; import { DatasourceLayers } from '../../types'; import { showingBar } from './metric_visualization'; -import { DEFAULT_MAX_COLUMNS, getDefaultColor, MetricVisualizationState } from './visualization'; +import { DEFAULT_MAX_COLUMNS, getDefaultColor } from './visualization'; +import { MetricVisualizationState } from './types'; +import { metricStateDefaults } from './constants'; // TODO - deduplicate with gauges? function computePaletteParams(params: CustomPaletteParams) { @@ -148,6 +150,10 @@ export const toExpression = ( progressDirection: showingBar(state) ? state.progressDirection || LayoutDirection.Vertical : undefined, + titlesTextAlign: state.titlesTextAlign ?? metricStateDefaults.titlesTextAlign, + valuesTextAlign: state.valuesTextAlign ?? metricStateDefaults.valuesTextAlign, + iconAlign: state.iconAlign ?? metricStateDefaults.iconAlign, + valueFontSize: state.valueFontMode ?? metricStateDefaults.valueFontMode, color: state.color || getDefaultColor(state, isMetricNumeric), icon: state.icon, palette: diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar.test.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar.test.tsx deleted file mode 100644 index 8b4ea8a39f3ab..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/metric/toolbar.test.tsx +++ /dev/null @@ -1,93 +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 React from 'react'; -import { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; -import { Toolbar } from './toolbar'; -import { MetricVisualizationState } from './visualization'; -import { createMockFramePublicAPI } from '../../mocks'; -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; - -describe('metric toolbar', () => { - const palette: PaletteOutput = { - type: 'palette', - name: 'foo', - params: { - rangeType: 'percent', - }, - }; - - const fullState: Required = { - layerId: 'first', - layerType: 'data', - metricAccessor: 'metric-col-id', - secondaryMetricAccessor: 'secondary-metric-col-id', - maxAccessor: 'max-metric-col-id', - breakdownByAccessor: 'breakdown-col-id', - collapseFn: 'sum', - subtitle: 'subtitle', - secondaryPrefix: 'extra-text', - progressDirection: 'vertical', - maxCols: 5, - color: 'static-color', - icon: 'compute', - palette, - showBar: true, - trendlineLayerId: 'second', - trendlineLayerType: 'metricTrendline', - trendlineMetricAccessor: 'trendline-metric-col-id', - trendlineSecondaryMetricAccessor: 'trendline-secondary-metric-col-id', - trendlineTimeAccessor: 'trendline-time-col-id', - trendlineBreakdownByAccessor: 'trendline-breakdown-col-id', - }; - - const frame = createMockFramePublicAPI(); - - const mockSetState = jest.fn(); - - const renderToolbar = (state: MetricVisualizationState) => { - return { ...render() }; - }; - - afterEach(() => mockSetState.mockClear()); - - describe('text options', () => { - it('sets a subtitle', async () => { - renderToolbar({ - ...fullState, - breakdownByAccessor: undefined, - }); - const textOptionsButton = screen.getByTestId('lnsLabelsButton'); - textOptionsButton.click(); - - const newSubtitle = 'new subtitle hey'; - const subtitleField = screen.getByDisplayValue('subtitle'); - // cannot use userEvent because the element cannot be clicked on - fireEvent.change(subtitleField, { target: { value: newSubtitle + ' 1' } }); - await waitFor(() => expect(mockSetState).toHaveBeenCalled()); - fireEvent.change(subtitleField, { target: { value: newSubtitle + ' 2' } }); - await waitFor(() => expect(mockSetState).toHaveBeenCalledTimes(2)); - fireEvent.change(subtitleField, { target: { value: newSubtitle + ' 3' } }); - await waitFor(() => expect(mockSetState).toHaveBeenCalledTimes(3)); - expect(mockSetState.mock.calls.map(([state]) => state.subtitle)).toMatchInlineSnapshot(` - Array [ - "new subtitle hey 1", - "new subtitle hey 2", - "new subtitle hey 3", - ] - `); - }); - - it('hides text options when has breakdown by', () => { - renderToolbar({ - ...fullState, - breakdownByAccessor: 'some-accessor', - }); - expect(screen.queryByTestId('lnsLabelsButton')).not.toBeInTheDocument(); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar.tsx deleted file mode 100644 index 2e7c1ca285d6e..0000000000000 --- a/x-pack/plugins/lens/public/visualizations/metric/toolbar.tsx +++ /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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiFlexGroup, EuiFormRow, EuiFieldText } from '@elastic/eui'; -import { useDebouncedValue } from '@kbn/visualization-utils'; -import { VisualizationToolbarProps } from '../../types'; -import { ToolbarPopover } from '../../shared_components'; -import { MetricVisualizationState } from './visualization'; - -export function Toolbar(props: VisualizationToolbarProps) { - const { state, setState } = props; - - const setSubtitle = useCallback( - (prefix: string) => setState({ ...state, subtitle: prefix }), - [setState, state] - ); - - const { inputValue: subtitleInputVal, handleInputChange: handleSubtitleChange } = - useDebouncedValue( - { - onChange: setSubtitle, - value: state.subtitle || '', - }, - { allowFalsyValue: true } - ); - - const hasBreakdownBy = Boolean(state.breakdownByAccessor); - - return ( - - {!hasBreakdownBy && ( - - - handleSubtitleChange(value)} - /> - - - )} - - ); -} diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/index.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/index.tsx new file mode 100644 index 0000000000000..503039f627974 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/index.tsx @@ -0,0 +1,8 @@ +/* + * 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 { Toolbar } from './toolbar'; diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/label_options_popover.test.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/label_options_popover.test.tsx new file mode 100644 index 0000000000000..1f205c60f6916 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/label_options_popover.test.tsx @@ -0,0 +1,94 @@ +/* + * 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 React from 'react'; +import { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { MetricVisualizationState } from '../types'; +import { LabelOptionsPopover } from './label_options_popover'; + +describe('LabelOptionsPopover', () => { + const palette: PaletteOutput = { + type: 'palette', + name: 'foo', + params: { + rangeType: 'percent', + }, + }; + + const fullState: Required = { + layerId: 'first', + layerType: 'data', + metricAccessor: 'metric-col-id', + secondaryMetricAccessor: 'secondary-metric-col-id', + maxAccessor: 'max-metric-col-id', + breakdownByAccessor: 'breakdown-col-id', + collapseFn: 'sum', + subtitle: 'subtitle', + secondaryPrefix: 'extra-text', + progressDirection: 'vertical', + maxCols: 5, + color: 'static-color', + icon: 'compute', + palette, + showBar: true, + trendlineLayerId: 'second', + trendlineLayerType: 'metricTrendline', + trendlineMetricAccessor: 'trendline-metric-col-id', + trendlineSecondaryMetricAccessor: 'trendline-secondary-metric-col-id', + trendlineTimeAccessor: 'trendline-time-col-id', + trendlineBreakdownByAccessor: 'trendline-breakdown-col-id', + titlesTextAlign: 'left', + valuesTextAlign: 'right', + iconAlign: 'left', + valueFontMode: 'default', + }; + + const mockSetState = jest.fn(); + + const renderToolbarOptions = (state: MetricVisualizationState) => { + return { + ...render(), + }; + }; + + afterEach(() => mockSetState.mockClear()); + + it('should set a subtitle', async () => { + renderToolbarOptions({ + ...fullState, + breakdownByAccessor: undefined, + }); + const labelOptionsButton = screen.getByTestId('lnsLabelsButton'); + labelOptionsButton.click(); + + const newSubtitle = 'new subtitle hey'; + const subtitleField = screen.getByDisplayValue('subtitle'); + // cannot use userEvent because the element cannot be clicked on + fireEvent.change(subtitleField, { target: { value: newSubtitle + ' 1' } }); + await waitFor(() => expect(mockSetState).toHaveBeenCalled()); + fireEvent.change(subtitleField, { target: { value: newSubtitle + ' 2' } }); + await waitFor(() => expect(mockSetState).toHaveBeenCalledTimes(2)); + fireEvent.change(subtitleField, { target: { value: newSubtitle + ' 3' } }); + await waitFor(() => expect(mockSetState).toHaveBeenCalledTimes(3)); + expect(mockSetState.mock.calls.map(([state]) => state.subtitle)).toMatchInlineSnapshot(` + Array [ + "new subtitle hey 1", + "new subtitle hey 2", + "new subtitle hey 3", + ] + `); + }); + + it('should disable labels options when Metric has breakdown by', () => { + renderToolbarOptions({ + ...fullState, + breakdownByAccessor: 'some-accessor', + }); + expect(screen.getByTestId('lnsLabelsButton')).toBeDisabled(); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/label_options_popover.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/label_options_popover.tsx new file mode 100644 index 0000000000000..ec7101a8dedb8 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/label_options_popover.tsx @@ -0,0 +1,72 @@ +/* + * 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 React, { FC, useCallback } from 'react'; + +import { EuiFormRow, EuiFieldText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { useDebouncedValue } from '@kbn/visualization-utils'; +import { TooltipWrapper } from '@kbn/visualization-utils'; +import { ToolbarPopover } from '../../../shared_components'; +import { MetricVisualizationState } from '../types'; + +export interface LabelOptionsPopoverProps { + state: MetricVisualizationState; + setState: (newState: MetricVisualizationState) => void; +} + +export const LabelOptionsPopover: FC = ({ state, setState }) => { + const setSubtitle = useCallback( + (prefix: string) => setState({ ...state, subtitle: prefix }), + [setState, state] + ); + + const { inputValue: subtitleInputVal, handleInputChange: handleSubtitleChange } = + useDebouncedValue( + { + onChange: setSubtitle, + value: state.subtitle || '', + }, + { allowFalsyValue: true } + ); + + const hasBreakdownBy = Boolean(state.breakdownByAccessor); + + return ( + + + + handleSubtitleChange(value)} + /> + + + + ); +}; diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/toolbar.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/toolbar.tsx new file mode 100644 index 0000000000000..1593d2dce5bf0 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/toolbar.tsx @@ -0,0 +1,28 @@ +/* + * 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 React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { VisualizationToolbarProps } from '../../../types'; +import { LabelOptionsPopover } from './label_options_popover'; +import { VisualOptionsPopover } from './visual_options_popover'; +import { MetricVisualizationState } from '../types'; + +export function Toolbar(props: VisualizationToolbarProps) { + const { state, setState } = props; + + return ( + + + + + + + + + ); +} diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/visual_options_popover.test.tsx new file mode 100644 index 0000000000000..06b253dc862b9 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/visual_options_popover.test.tsx @@ -0,0 +1,140 @@ +/* + * 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 React from 'react'; +import { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; +import { render, screen } from '@testing-library/react'; +import { MetricVisualizationState } from '../types'; +import { VisualOptionsPopover } from './visual_options_popover'; +import { EuiButtonGroupTestHarness } from '@kbn/test-eui-helpers'; + +jest.mock('lodash', () => ({ + ...jest.requireActual('lodash'), + debounce: (fn: unknown) => fn, +})); + +describe('VisualOptionsPopover', () => { + const palette: PaletteOutput = { + type: 'palette', + name: 'foo', + params: { + rangeType: 'percent', + }, + }; + + const fullState: Required = { + layerId: 'first', + layerType: 'data', + metricAccessor: 'metric-col-id', + secondaryMetricAccessor: 'secondary-metric-col-id', + maxAccessor: 'max-metric-col-id', + breakdownByAccessor: 'breakdown-col-id', + collapseFn: 'sum', + subtitle: 'subtitle', + secondaryPrefix: 'extra-text', + progressDirection: 'vertical', + maxCols: 5, + color: 'static-color', + icon: 'compute', + palette, + showBar: true, + trendlineLayerId: 'second', + trendlineLayerType: 'metricTrendline', + trendlineMetricAccessor: 'trendline-metric-col-id', + trendlineSecondaryMetricAccessor: 'trendline-secondary-metric-col-id', + trendlineTimeAccessor: 'trendline-time-col-id', + trendlineBreakdownByAccessor: 'trendline-breakdown-col-id', + titlesTextAlign: 'left', + valuesTextAlign: 'right', + iconAlign: 'left', + valueFontMode: 'default', + }; + + const mockSetState = jest.fn(); + + const renderToolbarOptions = (state: MetricVisualizationState) => { + return { + ...render(), + }; + }; + + afterEach(() => mockSetState.mockClear()); + + it('should set titlesTextAlign', async () => { + renderToolbarOptions({ ...fullState }); + const textOptionsButton = screen.getByTestId('lnsVisualOptionsButton'); + textOptionsButton.click(); + + const titlesAlignBtnGroup = new EuiButtonGroupTestHarness('lens-titles-alignment-btn'); + + titlesAlignBtnGroup.select('Right'); + titlesAlignBtnGroup.select('Center'); + titlesAlignBtnGroup.select('Left'); + + expect(mockSetState.mock.calls.map(([s]) => s.titlesTextAlign)).toEqual([ + 'right', + 'center', + 'left', + ]); + }); + + it('should set valuesTextAlign', async () => { + renderToolbarOptions({ ...fullState }); + const textOptionsButton = screen.getByTestId('lnsVisualOptionsButton'); + textOptionsButton.click(); + + const valueAlignBtnGroup = new EuiButtonGroupTestHarness('lens-values-alignment-btn'); + + valueAlignBtnGroup.select('Center'); + valueAlignBtnGroup.select('Left'); + valueAlignBtnGroup.select('Right'); + + expect(mockSetState.mock.calls.map(([s]) => s.valuesTextAlign)).toEqual([ + 'center', + 'left', + 'right', + ]); + }); + + it('should set valueFontMode', async () => { + renderToolbarOptions({ ...fullState }); + const textOptionsButton = screen.getByTestId('lnsVisualOptionsButton'); + textOptionsButton.click(); + + const modeBtnGroup = new EuiButtonGroupTestHarness('lens-value-font-mode-btn'); + + expect(modeBtnGroup.selected.textContent).toBe('Default'); + + modeBtnGroup.select('Fit'); + modeBtnGroup.select('Default'); + + expect(mockSetState.mock.calls.map(([s]) => s.valueFontMode)).toEqual(['fit', 'default']); + }); + + it('should set iconAlign', async () => { + renderToolbarOptions({ ...fullState, icon: 'sortUp' }); + const textOptionsButton = screen.getByTestId('lnsVisualOptionsButton'); + textOptionsButton.click(); + + const iconAlignBtnGroup = new EuiButtonGroupTestHarness('lens-icon-alignment-btn'); + + expect(iconAlignBtnGroup.selected.textContent).toBe('Left'); + + iconAlignBtnGroup.select('Right'); + iconAlignBtnGroup.select('Left'); + + expect(mockSetState.mock.calls.map(([s]) => s.iconAlign)).toEqual(['right', 'left']); + }); + + it.each([undefined, 'empty'])('should hide iconAlign option when icon is %j', async (icon) => { + renderToolbarOptions({ ...fullState, icon }); + const textOptionsButton = screen.getByTestId('lnsVisualOptionsButton'); + textOptionsButton.click(); + + expect(screen.queryByTestId('lens-icon-alignment-btn')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/lens/public/visualizations/metric/toolbar/visual_options_popover.tsx b/x-pack/plugins/lens/public/visualizations/metric/toolbar/visual_options_popover.tsx new file mode 100644 index 0000000000000..607bba17afc24 --- /dev/null +++ b/x-pack/plugins/lens/public/visualizations/metric/toolbar/visual_options_popover.tsx @@ -0,0 +1,290 @@ +/* + * 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 React, { FC } from 'react'; + +import { EuiFormRow, EuiIconTip, EuiButtonGroup } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { MetricStyle } from '@elastic/charts'; +import { ToolbarPopover } from '../../../shared_components'; +import { MetricVisualizationState, ValueFontMode } from '../types'; +import { metricStateDefaults } from '../constants'; + +export interface VisualOptionsPopoverProps { + state: MetricVisualizationState; + setState: (newState: MetricVisualizationState) => void; +} + +export const VisualOptionsPopover: FC = ({ state, setState }) => { + return ( + + { + setState({ ...state, titlesTextAlign }); + }} + /> + { + setState({ ...state, valuesTextAlign }); + }} + /> + {state.icon && state.icon !== 'empty' && ( + { + setState({ ...state, iconAlign }); + }} + /> + )} + { + setState({ ...state, valueFontMode: value }); + }} + /> + + ); +}; + +const valueFontModes: Array<{ + id: ValueFontMode; + label: string; +}> = [ + { + id: 'default', + label: i18n.translate('xpack.lens.metric.toolbarVisOptions.default', { + defaultMessage: 'Default', + }), + }, + { + id: 'fit', + label: i18n.translate('xpack.lens.metric.toolbarVisOptions.fit', { + defaultMessage: 'Fit', + }), + }, +]; + +function ValueFontOption({ + value, + onChange, +}: { + value: typeof valueFontModes[number]['id']; + onChange: (mode: ValueFontMode) => void; +}) { + const label = i18n.translate('xpack.lens.metric.toolbarVisOptions.valueFontSize', { + defaultMessage: 'Value fontSize', + }); + + return ( + + {label}{' '} + + + } + > + { + onChange(mode as ValueFontMode); + }} + /> + + ); +} + +const alignmentOptions: Array<{ + id: MetricStyle['titlesTextAlign'] | MetricStyle['valuesTextAlign']; + label: string; +}> = [ + { + id: 'left', + label: i18n.translate('xpack.lens.shared.left', { + defaultMessage: 'Left', + }), + }, + { + id: 'center', + label: i18n.translate('xpack.lens.shared.center', { + defaultMessage: 'Center', + }), + }, + { + id: 'right', + label: i18n.translate('xpack.lens.shared.right', { + defaultMessage: 'Right', + }), + }, +]; + +function TitlesAlignmentOption({ + value, + onChange, +}: { + value: MetricStyle['titlesTextAlign']; + onChange: (alignment: MetricStyle['titlesTextAlign']) => void; +}) { + const label = i18n.translate('xpack.lens.metric.toolbarVisOptions.titlesAlignment', { + defaultMessage: 'Titles alignment', + }); + + return ( + + {label}{' '} + + + } + > + { + onChange(alignment as MetricStyle['titlesTextAlign']); + }} + /> + + ); +} + +function ValuesAlignmentOption({ + value, + onChange, +}: { + value: MetricStyle['valuesTextAlign']; + onChange: (alignment: MetricStyle['valuesTextAlign']) => void; +}) { + const label = i18n.translate('xpack.lens.metric.toolbarVisOptions.valuesAlignment', { + defaultMessage: 'Values alignment', + }); + + return ( + + {label}{' '} + + + } + > + { + onChange(alignment as MetricStyle['valuesTextAlign']); + }} + /> + + ); +} + +const iconAlignmentOptions: Array<{ + id: MetricStyle['titlesTextAlign'] | MetricStyle['valuesTextAlign']; + label: string; +}> = [ + { + id: 'left', + label: i18n.translate('xpack.lens.shared.left', { + defaultMessage: 'Left', + }), + }, + { + id: 'right', + label: i18n.translate('xpack.lens.shared.right', { + defaultMessage: 'Right', + }), + }, +]; + +function IconAlignmentOption({ + value, + onChange, +}: { + value: MetricStyle['iconAlign']; + onChange: (alignment: MetricStyle['iconAlign']) => void; +}) { + const label = i18n.translate('xpack.lens.metric.toolbarVisOptions.iconAlignment', { + defaultMessage: 'Icon alignment', + }); + + return ( + + { + onChange(alignment as MetricStyle['iconAlign']); + }} + /> + + ); +} diff --git a/x-pack/plugins/lens/public/visualizations/metric/types.ts b/x-pack/plugins/lens/public/visualizations/metric/types.ts index d25a4b1b33396..774c1572ce73b 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/types.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/types.ts @@ -5,11 +5,13 @@ * 2.0. */ -import type { LayoutDirection } from '@elastic/charts'; +import type { LayoutDirection, MetricStyle } from '@elastic/charts'; import type { PaletteOutput, CustomPaletteParams } from '@kbn/coloring'; import type { CollapseFunction } from '@kbn/visualizations-plugin/common'; import type { LayerType } from '../../../common/types'; +export type ValueFontMode = Exclude; + export interface MetricVisualizationState { layerId: string; layerType: LayerType; @@ -24,7 +26,12 @@ export interface MetricVisualizationState { secondaryPrefix?: string; progressDirection?: LayoutDirection; showBar?: boolean; + titlesTextAlign?: MetricStyle['titlesTextAlign']; + valuesTextAlign?: MetricStyle['valuesTextAlign']; + iconAlign?: MetricStyle['iconAlign']; + valueFontMode?: ValueFontMode; color?: string; + icon?: string; palette?: PaletteOutput; maxCols?: number; diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts index a907fa0cae917..af9039740f3eb 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts @@ -19,10 +19,11 @@ import { Visualization, } from '../../types'; import { GROUP_ID } from './constants'; -import { getMetricVisualization, MetricVisualizationState } from './visualization'; +import { getMetricVisualization } from './visualization'; import { themeServiceMock } from '@kbn/core/public/mocks'; import { Ast } from '@kbn/interpreter'; import { LayoutDirection } from '@elastic/charts'; +import { MetricVisualizationState } from './types'; const paletteService = chartPluginMock.createPaletteRegistry(); const theme = themeServiceMock.createStartContract(); @@ -76,6 +77,10 @@ describe('metric visualization', () => { color: 'static-color', palette, showBar: false, + titlesTextAlign: 'left', + valuesTextAlign: 'right', + iconAlign: 'left', + valueFontMode: 'default', }; const fullStateWTrend: Required = { @@ -316,6 +321,9 @@ describe('metric visualization', () => { "icon": Array [ "empty", ], + "iconAlign": Array [ + "left", + ], "inspectorTableId": Array [ "first", ], @@ -353,7 +361,16 @@ describe('metric visualization', () => { "subtitle": Array [ "subtitle", ], + "titlesTextAlign": Array [ + "left", + ], "trendline": Array [], + "valueFontSize": Array [ + "default", + ], + "valuesTextAlign": Array [ + "right", + ], }, "function": "metricVis", "type": "function", @@ -380,6 +397,9 @@ describe('metric visualization', () => { "icon": Array [ "empty", ], + "iconAlign": Array [ + "left", + ], "inspectorTableId": Array [ "first", ], @@ -420,7 +440,16 @@ describe('metric visualization', () => { "subtitle": Array [ "subtitle", ], + "titlesTextAlign": Array [ + "left", + ], "trendline": Array [], + "valueFontSize": Array [ + "default", + ], + "valuesTextAlign": Array [ + "right", + ], }, "function": "metricVis", "type": "function", @@ -778,8 +807,12 @@ describe('metric visualization', () => { expect(visualization.clearLayer(fullState, 'some-id', 'indexPattern1')).toMatchInlineSnapshot(` Object { "icon": "empty", + "iconAlign": "left", "layerId": "first", "layerType": "data", + "titlesTextAlign": "left", + "valueFontMode": "default", + "valuesTextAlign": "right", } `); }); diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx index f60b1da51505d..3a780e5a20f75 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx @@ -7,16 +7,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { PaletteOutput, PaletteRegistry, CustomPaletteParams } from '@kbn/coloring'; +import { PaletteRegistry } from '@kbn/coloring'; import { ThemeServiceStart } from '@kbn/core/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; -import { LayoutDirection } from '@elastic/charts'; import { euiLightVars, euiThemeVars } from '@kbn/ui-theme'; import { IconChartMetric } from '@kbn/chart-icons'; import { AccessorConfig } from '@kbn/visualization-ui-components'; import { isNumericFieldForDatatable } from '../../../common/expressions/datatable/utils'; -import { CollapseFunction } from '../../../common/expressions'; -import type { LayerType } from '../../../common/types'; import { layerTypes } from '../../../common/layer_types'; import type { FormBasedPersistedState } from '../../datasources/form_based/types'; import { getSuggestions } from './suggestions'; @@ -35,6 +32,7 @@ import { generateId } from '../../id_generator'; import { toExpression } from './to_expression'; import { nonNullable } from '../../utils'; import { METRIC_NUMERIC_MAX } from '../../user_messages_ids'; +import { MetricVisualizationState } from './types'; export const DEFAULT_MAX_COLUMNS = 3; @@ -49,33 +47,6 @@ export const getDefaultColor = (state: MetricVisualizationState, isMetricNumeric : euiThemeVars.euiColorEmptyShade; }; -export interface MetricVisualizationState { - layerId: string; - layerType: LayerType; - metricAccessor?: string; - secondaryMetricAccessor?: string; - maxAccessor?: string; - breakdownByAccessor?: string; - // the dimensions can optionally be single numbers - // computed by collapsing all rows - collapseFn?: CollapseFunction; - subtitle?: string; - secondaryPrefix?: string; - progressDirection?: LayoutDirection; - showBar?: boolean; - color?: string; - icon?: string; - palette?: PaletteOutput; - maxCols?: number; - - trendlineLayerId?: string; - trendlineLayerType?: LayerType; - trendlineTimeAccessor?: string; - trendlineMetricAccessor?: string; - trendlineSecondaryMetricAccessor?: string; - trendlineBreakdownByAccessor?: string; -} - export const supportedDataTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); const isSupportedMetric = (op: OperationMetadata) => diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx index b8b244daf85d2..5fd35a780cda5 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/visual_options_popover/index.tsx @@ -79,7 +79,7 @@ export const VisualOptionsPopover: React.FC = ({ return ( { license = await fetcher(); expect(license.error).toEqual('woups'); }); + + it('logs a warning in case of successful call without license', async () => { + clusterClient.asInternalUser.xpack.info.mockResponse({ + license: undefined, + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + const license = await fetcher(); + expect(license.isAvailable).toEqual(false); + expect(loggerMock.collect(logger).warn.map((args) => args[0])).toMatchInlineSnapshot(` + Array [ + "License information fetched from Elasticsearch, but no license is available", + ] + `); + }); + + it('logs a warning in case of successful call with a missing license', async () => { + clusterClient.asInternalUser.xpack.info.mockResponse({ + license: buildRawLicense({ + type: 'missing', + }), + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + const license = await fetcher(); + expect(license.isAvailable).toEqual(false); + expect(loggerMock.collect(logger).warn.map((args) => args[0])).toMatchInlineSnapshot(` + Array [ + "License information fetched from Elasticsearch, but no license is available", + ] + `); + }); }); diff --git a/x-pack/plugins/licensing/server/license_fetcher.ts b/x-pack/plugins/licensing/server/license_fetcher.ts index 43d9c204bbf66..56a89fe221c9d 100644 --- a/x-pack/plugins/licensing/server/license_fetcher.ts +++ b/x-pack/plugins/licensing/server/license_fetcher.ts @@ -45,6 +45,10 @@ export const getLicenseFetcher = ({ ? normalizeFeatures(response.features) : undefined; + if (!normalizedLicense) { + logger.warn('License information fetched from Elasticsearch, but no license is available'); + } + const signature = sign({ license: normalizedLicense, features: normalizedFeatures, diff --git a/x-pack/plugins/maps/kibana.jsonc b/x-pack/plugins/maps/kibana.jsonc index 881d557e5e305..b042d0250b0c2 100644 --- a/x-pack/plugins/maps/kibana.jsonc +++ b/x-pack/plugins/maps/kibana.jsonc @@ -49,7 +49,7 @@ "usageCollection", "unifiedSearch", "fieldFormats", - "textBasedLanguages", + "esql", "savedObjects", ], "extraPublicDirs": [ diff --git a/x-pack/plugins/maps/public/classes/sources/esql_source/esql_editor.tsx b/x-pack/plugins/maps/public/classes/sources/esql_source/esql_editor.tsx index 8ed04f61adb5d..5cfaf5abfcb72 100644 --- a/x-pack/plugins/maps/public/classes/sources/esql_source/esql_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/esql_source/esql_editor.tsx @@ -11,7 +11,7 @@ import { isEqual } from 'lodash'; import useMountedState from 'react-use/lib/useMountedState'; import type { AggregateQuery } from '@kbn/es-query'; import type { ESQLColumn } from '@kbn/es-types'; -import { TextBasedLangEditor } from '@kbn/text-based-languages/public'; +import { TextBasedLangEditor } from '@kbn/esql/public'; import { getESQLMeta, verifyGeometryColumn } from './esql_utils'; interface Props { diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 5821e1e82a21e..cfd635f23d58d 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -78,7 +78,7 @@ "@kbn/search-response-warnings", "@kbn/calculate-width-from-char-count", "@kbn/content-management-table-list-view-common", - "@kbn/text-based-languages", + "@kbn/esql", "@kbn/es-types", "@kbn/data-service", "@kbn/code-editor", diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/inference_models.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/inference_models.ts index 7f4ba9dbf23f7..2895d1fed4336 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/inference_models.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/inference_models.ts @@ -18,17 +18,18 @@ export function inferenceModelsApiProvider(httpService: HttpService) { * @param taskType - Inference Task type. Either sparse_embedding or text_embedding * @param modelConfig - Model configuration based on service type */ - createInferenceEndpoint( + async createInferenceEndpoint( inferenceId: string, taskType: InferenceTaskType, modelConfig: ModelConfig ) { - return httpService.http({ + const result = await httpService.http({ path: `${ML_INTERNAL_BASE_PATH}/_inference/${taskType}/${inferenceId}`, method: 'PUT', body: JSON.stringify(modelConfig), version: '1', }); + return result; }, }; } diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts index 3070c3a4b7441..1d3e0f97ff4ab 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/trained_models.ts @@ -177,6 +177,18 @@ export function trainedModelsApiProvider(httpService: HttpService) { }); }, + /** + * Gets model config based on the cluster OS and CPU architecture. + */ + getCuratedModelConfig(modelName: string, options?: GetModelDownloadConfigOptions) { + return httpService.http({ + path: `${ML_INTERNAL_BASE_PATH}/trained_models/curated_model_config/${modelName}`, + method: 'GET', + ...(options ? { query: options as HttpFetchQuery } : {}), + version: '1', + }); + }, + getTrainedModelsNodesOverview() { return httpService.http({ path: `${ML_INTERNAL_BASE_PATH}/model_management/nodes_overview`, diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 0a7e44959a262..ae41b31d3eaaf 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -269,7 +269,7 @@ export class MlPlugin implements Plugin { ); } - if (fullLicense) { + if (fullLicense && mlCapabilities.canGetMlInfo) { registerMlUiActions(pluginsSetup.uiActions, core); if (this.enabledFeatures.ad) { diff --git a/x-pack/plugins/ml/server/routes/inference_models.ts b/x-pack/plugins/ml/server/routes/inference_models.ts index 1ab5d576181ef..29f687ede932d 100644 --- a/x-pack/plugins/ml/server/routes/inference_models.ts +++ b/x-pack/plugins/ml/server/routes/inference_models.ts @@ -12,6 +12,7 @@ import { createInferenceSchema } from './schemas/inference_schema'; import { modelsProvider } from '../models/model_management'; import { wrapError } from '../client/error_wrapper'; import { ML_INTERNAL_BASE_PATH } from '../../common/constants/app'; +import { syncSavedObjectsFactory } from '../saved_objects'; export function inferenceModelRoutes( { router, routeGuard }: RouteInitialization, @@ -42,20 +43,24 @@ export function inferenceModelRoutes( }, }, }, - routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, request, response }) => { - try { - const { inferenceId, taskType } = request.params; - const body = await modelsProvider(client, mlClient, cloud).createInferenceEndpoint( - inferenceId, - taskType as InferenceTaskType, - request.body as InferenceModelConfig - ); - return response.ok({ - body, - }); - } catch (e) { - return response.customError(wrapError(e)); + routeGuard.fullLicenseAPIGuard( + async ({ client, mlClient, request, response, mlSavedObjectService }) => { + try { + const { inferenceId, taskType } = request.params; + const body = await modelsProvider(client, mlClient, cloud).createInferenceEndpoint( + inferenceId, + taskType as InferenceTaskType, + request.body as InferenceModelConfig + ); + const { syncSavedObjects } = syncSavedObjectsFactory(client, mlSavedObjectService); + await syncSavedObjects(false); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } } - }) + ) ); } diff --git a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts index a69b1384e2c7e..662b3313a06e3 100644 --- a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts @@ -96,3 +96,9 @@ export const createIngestPipelineSchema = schema.object({ export const modelDownloadsQuery = schema.object({ version: schema.maybe(schema.oneOf([schema.literal('1'), schema.literal('2')])), }); + +export const curatedModelsParamsSchema = schema.object({ + modelName: schema.string(), +}); + +export const curatedModelsQuerySchema = schema.object({ version: schema.maybe(schema.number()) }); diff --git a/x-pack/plugins/ml/server/routes/trained_models.ts b/x-pack/plugins/ml/server/routes/trained_models.ts index cdd4d8128c219..5b2441435268d 100644 --- a/x-pack/plugins/ml/server/routes/trained_models.ts +++ b/x-pack/plugins/ml/server/routes/trained_models.ts @@ -10,7 +10,11 @@ import { groupBy } from 'lodash'; import { schema } from '@kbn/config-schema'; import type { ErrorType } from '@kbn/ml-error-utils'; import type { CloudSetup } from '@kbn/cloud-plugin/server'; -import type { ElserVersion, InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; +import type { + ElasticCuratedModelName, + ElserVersion, + InferenceAPIConfigResponse, +} from '@kbn/ml-trained-models-utils'; import { isDefined } from '@kbn/ml-is-defined'; import type { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; import { type MlFeatures, ML_INTERNAL_BASE_PATH } from '../../common/constants/app'; @@ -30,6 +34,8 @@ import { updateDeploymentParamsSchema, createIngestPipelineSchema, modelDownloadsQuery, + curatedModelsParamsSchema, + curatedModelsQuerySchema, } from './schemas/inference_schema'; import type { PipelineDefinition } from '../../common/types/trained_models'; import { type TrainedModelConfigResponse } from '../../common/types/trained_models'; @@ -920,6 +926,49 @@ export function trainedModelsRoutes( try { const body = await modelsProvider(client, mlClient, cloud).getModelsDownloadStatus(); + return response.ok({ + body, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + } + ) + ); + + /** + * @apiGroup TrainedModels + * + * @api {get} /internal/ml/trained_models/curated_model_config Gets curated model config + * @apiName ModelsCuratedConfigs + * @apiDescription Gets curated model config for the specified model based on cluster architecture + */ + router.versioned + .get({ + path: `${ML_INTERNAL_BASE_PATH}/trained_models/curated_model_config/{modelName}`, + access: 'internal', + options: { + tags: ['access:ml:canGetTrainedModels'], + }, + }) + .addVersion( + { + version: '1', + validate: { + request: { + params: curatedModelsParamsSchema, + query: curatedModelsQuerySchema, + }, + }, + }, + routeGuard.fullLicenseAPIGuard( + async ({ client, mlClient, request, response, mlSavedObjectService }) => { + try { + const body = await modelsProvider(client, mlClient, cloud).getCuratedModelConfig( + request.params.modelName as ElasticCuratedModelName, + { version: request.query.version as ElserVersion } + ); + return response.ok({ body, }); diff --git a/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap b/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap index 6e0623564bab3..24d9d52224fa4 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap +++ b/x-pack/plugins/monitoring/server/lib/metrics/__snapshots__/metrics.test.js.snap @@ -36,7 +36,7 @@ Object { "dateHistogramSubAggs": undefined, "derivative": true, "derivativeNormalizedUnits": true, - "description": "HTTP Requests received by agent configuration managemen", + "description": "HTTP Requests received by agent configuration management", "docType": undefined, "field": "beats_stats.metrics.apm-server.acm.request.count", "fieldSource": undefined, diff --git a/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.ts b/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.ts index 8cd3941be1338..1593ecd2beba4 100644 --- a/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.ts +++ b/x-pack/plugins/monitoring/server/lib/metrics/apm/metrics.ts @@ -612,7 +612,7 @@ export const metrics = { defaultMessage: 'Count', }), description: i18n.translate('xpack.monitoring.metrics.apm.acmRequest.countTitleDescription', { - defaultMessage: 'HTTP Requests received by agent configuration managemen', + defaultMessage: 'HTTP Requests received by agent configuration management', }), }), apm_cgroup_memory_usage: new ApmMetric({ diff --git a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/service_inventory/service_inventory.cy.ts b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/service_inventory/service_inventory.cy.ts index 7fdb8724243a3..3d7882176f64e 100644 --- a/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/service_inventory/service_inventory.cy.ts +++ b/x-pack/plugins/observability_solution/apm/ftr_e2e/cypress/e2e/service_inventory/service_inventory.cy.ts @@ -91,7 +91,7 @@ describe('Service inventory', () => { it('with the correct environment when changing the environment', () => { cy.wait(mainAliasNames); - cy.getByTestSubj('environmentFilter').type('production'); + cy.getByTestSubj('environmentFilter').type('{selectall}production'); cy.contains('button', 'production').click(); @@ -103,6 +103,7 @@ describe('Service inventory', () => { it('when selecting a different time range and clicking the update button', () => { cy.wait(mainAliasNames); + cy.getByTestSubj('apmServiceGroupsTourDismissButton').click(); cy.selectAbsoluteTimeRange( moment(timeRange.rangeFrom).subtract(5, 'm').toISOString(), diff --git a/x-pack/plugins/observability_solution/apm/kibana.jsonc b/x-pack/plugins/observability_solution/apm/kibana.jsonc index aa87258afbe36..c6a53232aaec0 100644 --- a/x-pack/plugins/observability_solution/apm/kibana.jsonc +++ b/x-pack/plugins/observability_solution/apm/kibana.jsonc @@ -30,7 +30,8 @@ "lens", "maps", "uiActions", - "logsDataAccess" + "logsDataAccess", + "entityManager" ], "optionalPlugins": [ "actions", @@ -52,13 +53,6 @@ "cases", "observabilityAIAssistant" ], - "requiredBundles": [ - "fleet", - "kibanaReact", - "kibanaUtils", - "ml", - "observability", - "maps" - ] + "requiredBundles": ["fleet", "kibanaReact", "kibanaUtils", "ml", "observability", "maps"] } } diff --git a/x-pack/plugins/observability_solution/apm/public/assets/services_inventory.png b/x-pack/plugins/observability_solution/apm/public/assets/services_inventory.png new file mode 100644 index 0000000000000..fc9347c5b5430 Binary files /dev/null and b/x-pack/plugins/observability_solution/apm/public/assets/services_inventory.png differ diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/index.tsx index d00dc0a6e2d6a..ed8f9e361c321 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/index.tsx @@ -5,24 +5,39 @@ * 2.0. */ import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui'; import { isEmpty } from 'lodash'; -import { apmEnableMultiSignal } from '@kbn/observability-plugin/common'; -import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { ApmServiceInventory } from './apm_signal_inventory'; import { MultiSignalInventory } from './multi_signal_inventory'; import { useApmParams } from '../../../hooks/use_apm_params'; +import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context'; -export const ServiceInventory = () => { - const { core } = useApmPluginContext(); - const isMultiSignalEnabled = core.uiSettings.get(apmEnableMultiSignal, false); +export function ServiceInventory() { + const { isEntityManagerEnabled, isEnablementPending } = useEntityManagerEnablementContext(); const { query: { serviceGroup }, } = useApmParams('/services'); - return isMultiSignalEnabled && isEmpty(serviceGroup) ? ( + if (isEnablementPending) { + return ( + } + title={ +

    + {i18n.translate('xpack.apm.loadingService', { + defaultMessage: 'Loading services', + })} +

    + } + /> + ); + } + + return isEntityManagerEnabled && isEmpty(serviceGroup) ? ( ) : ( ); -}; +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/index.tsx index af2282ea19219..01b8b9af7985f 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/index.tsx @@ -8,9 +8,10 @@ import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { v4 as uuidv4 } from 'uuid'; +import { ApmDocumentType } from '../../../../../common/document_type'; import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; import { useApmParams } from '../../../../hooks/use_apm_params'; -import { useFetcher } from '../../../../hooks/use_fetcher'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useTimeRange } from '../../../../hooks/use_time_range'; import { EmptyMessage } from '../../../shared/empty_message'; import { SearchBar } from '../../../shared/search_bar/search_bar'; @@ -22,6 +23,9 @@ import { MultiSignalServicesTable, ServiceInventoryFieldName, } from './table/multi_signal_services_table'; +import { ServiceListItem } from '../../../../../common/service_inventory'; +import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_preferred_data_source_and_bucket_size'; +import { useProgressiveFetcher } from '../../../../hooks/use_progressive_fetcher'; type MainStatisticsApiResponse = APIReturnType<'GET /internal/apm/entities/services'>; @@ -74,9 +78,72 @@ function useServicesEntitiesMainStatisticsFetcher() { return { mainStatisticsData: data, mainStatisticsStatus: status }; } -export const MultiSignalInventory = () => { +function useServicesEntitiesDetailedStatisticsFetcher({ + mainStatisticsFetch, + services, +}: { + mainStatisticsFetch: ReturnType; + services: ServiceListItem[]; +}) { + const { + query: { rangeFrom, rangeTo, environment, kuery }, + } = useApmParams('/services'); + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + + const dataSourceOptions = usePreferredDataSourceAndBucketSize({ + start, + end, + kuery, + type: ApmDocumentType.ServiceTransactionMetric, + numBuckets: 20, + }); + + const { mainStatisticsData, mainStatisticsStatus } = mainStatisticsFetch; + + const timeseriesDataFetch = useProgressiveFetcher( + (callApmApi) => { + const serviceNames = services.map(({ serviceName }) => serviceName); + + if ( + start && + end && + serviceNames.length > 0 && + mainStatisticsStatus === FETCH_STATUS.SUCCESS && + dataSourceOptions + ) { + return callApmApi('POST /internal/apm/entities/services/detailed_statistics', { + params: { + query: { + environment, + kuery, + start, + end, + documentType: dataSourceOptions.source.documentType, + rollupInterval: dataSourceOptions.source.rollupInterval, + bucketSizeInSeconds: dataSourceOptions.bucketSizeInSeconds, + }, + body: { + // Service name is sorted to guarantee the same order every time this API is called so the result can be cached. + serviceNames: JSON.stringify(serviceNames.sort()), + }, + }, + }); + } + }, + // only fetches detailed statistics when requestId is invalidated by main statistics api call or offset is changed + // eslint-disable-next-line react-hooks/exhaustive-deps + [mainStatisticsData.requestId, services], + { preservePreviousData: false } + ); + + return { timeseriesDataFetch }; +} + +export function MultiSignalInventory() { const [searchQuery, setSearchQuery] = React.useState(''); const { mainStatisticsData, mainStatisticsStatus } = useServicesEntitiesMainStatisticsFetcher(); + const mainStatisticsFetch = useServicesEntitiesMainStatisticsFetcher(); const initialSortField = ServiceInventoryFieldName.Throughput; @@ -86,6 +153,11 @@ export const MultiSignalInventory = () => { fieldsToSearch: [ServiceInventoryFieldName.ServiceName], }); + const { timeseriesDataFetch } = useServicesEntitiesDetailedStatisticsFetcher({ + mainStatisticsFetch, + services: mainStatisticsData.services, + }); + return ( <> @@ -110,6 +182,8 @@ export const MultiSignalInventory = () => { initialSortField={initialSortField} initialPageSize={INITIAL_PAGE_SIZE} initialSortDirection={INITIAL_SORT_DIRECTION} + timeseriesData={timeseriesDataFetch?.data} + timeseriesDataLoading={timeseriesDataFetch.status === FETCH_STATUS.LOADING} noItemsMessage={ { ); -}; +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx index b68201894b0d3..a5661e36141ac 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx @@ -31,15 +31,26 @@ import { TruncateWithTooltip } from '../../../../shared/truncate_with_tooltip'; import { ServiceInventoryFieldName } from './multi_signal_services_table'; import { EntityServiceListItem, SignalTypes } from '../../../../../../common/entities/types'; import { isApmSignal } from '../../../../../utils/get_signal_type'; +import { APIReturnType } from '../../../../../services/rest/create_call_apm_api'; + +type ServicesDetailedStatisticsAPIResponse = + APIReturnType<'POST /internal/apm/entities/services/detailed_statistics'>; + export function getServiceColumns({ query, breakpoints, link, + timeseriesDataLoading, + timeseriesData, }: { query: TypeOf['query']; breakpoints: Breakpoints; link: any; + timeseriesDataLoading: boolean; + timeseriesData?: ServicesDetailedStatisticsAPIResponse; }): Array> { + const { isSmall, isLarge } = breakpoints; + const showWhenSmallOrGreaterThanLarge = isSmall || !isLarge; return [ { field: ServiceInventoryFieldName.ServiceName, @@ -90,17 +101,18 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics, signalTypes }) => { + render: (_, { metrics, serviceName, signalTypes }) => { const { currentPeriodColor } = getTimeSeriesColor(ChartType.LATENCY_AVG); return !isApmSignal(signalTypes) ? ( ) : ( ); }, @@ -113,17 +125,18 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics, signalTypes }) => { + render: (_, { metrics, serviceName, signalTypes }) => { const { currentPeriodColor } = getTimeSeriesColor(ChartType.THROUGHPUT); return !isApmSignal(signalTypes) ? ( ) : ( ); }, @@ -136,17 +149,18 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics, signalTypes }) => { + render: (_, { metrics, serviceName, signalTypes }) => { const { currentPeriodColor } = getTimeSeriesColor(ChartType.FAILED_TRANSACTION_RATE); return !isApmSignal(signalTypes) ? ( ) : ( ); }, @@ -159,15 +173,16 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics }) => { + render: (_, { metrics, serviceName }) => { const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_RATE); return ( ); }, @@ -180,15 +195,16 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics }) => { + render: (_, { metrics, serviceName }) => { const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_ERROR_RATE); return ( ); }, diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/multi_signal_services_table.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/multi_signal_services_table.tsx index deffe7a1de5ba..5320fef8f22db 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/multi_signal_services_table.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/multi_signal_services_table.tsx @@ -17,6 +17,8 @@ import { ManagedTable } from '../../../../shared/managed_table'; import { getServiceColumns } from './get_service_columns'; type MainStatisticsApiResponse = APIReturnType<'GET /internal/apm/entities/services'>; +type ServicesDetailedStatisticsAPIResponse = + APIReturnType<'POST /internal/apm/entities/services/detailed_statistics'>; export enum ServiceInventoryFieldName { ServiceName = 'serviceName', @@ -35,6 +37,8 @@ interface Props { initialSortDirection: 'asc' | 'desc'; noItemsMessage: React.ReactNode; data: MainStatisticsApiResponse['services']; + timeseriesDataLoading: boolean; + timeseriesData?: ServicesDetailedStatisticsAPIResponse; } export function MultiSignalServicesTable({ @@ -44,6 +48,8 @@ export function MultiSignalServicesTable({ initialPageSize, initialSortDirection, noItemsMessage, + timeseriesDataLoading, + timeseriesData, }: Props) { const breakpoints = useBreakpoints(); const { query } = useApmParams('/services'); @@ -55,8 +61,10 @@ export function MultiSignalServicesTable({ query: omit(query, 'page', 'pageSize', 'sortDirection', 'sortField'), breakpoints, link, + timeseriesDataLoading, + timeseriesData, }); - }, [query, link, breakpoints]); + }, [query, breakpoints, link, timeseriesDataLoading, timeseriesData]); return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/apm_route_config.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/apm_route_config.tsx index 4fe5f5f583f1b..05d6b5f227e39 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/apm_route_config.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/apm_route_config.tsx @@ -101,6 +101,7 @@ const apmRoutes = { environmentFilter={false} showServiceGroupSaveButton={false} showServiceGroupsNav + showEnablementCallout selectedNavButton="serviceGroups" > diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/index.tsx index b331027a79d0f..02d73e7df75bc 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/app_root/index.tsx @@ -42,6 +42,7 @@ import { RedirectWithDefaultEnvironment } from './redirect_with_default_environm import { RedirectWithOffset } from './redirect_with_offset'; import { ScrollToTopOnPathChange } from './scroll_to_top_on_path_change'; import { UpdateExecutionContextOnRouteChange } from './update_execution_context_on_route_change'; +import { EntityManagerEnablementContextProvider } from '../../../context/entity_manager_context/entity_manager_context'; const storage = new Storage(localStorage); @@ -83,15 +84,17 @@ export function ApmAppRoot({ - - - - - - - - - + + + + + + + + + + + diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_main_template.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_main_template.tsx index afe1de2490e07..36ab11d45851a 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_main_template.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/apm_main_template.tsx @@ -23,6 +23,7 @@ import { ServiceGroupsButtonGroup } from '../../app/service_groups/service_group import { ApmEnvironmentFilter } from '../../shared/environment_filter'; import { getNoDataConfig } from './no_data_config'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { EntityEnablement } from '../../shared/entity_enablement'; // Paths that must skip the no data screen const bypassNoDataScreenPaths = ['/settings', '/diagnostics']; @@ -45,6 +46,7 @@ export function ApmMainTemplate({ environmentFilter = true, showServiceGroupSaveButton = false, showServiceGroupsNav = false, + showEnablementCallout = false, environmentFilterInTemplate = true, selectedNavButton, ...pageTemplateProps @@ -55,6 +57,7 @@ export function ApmMainTemplate({ environmentFilter?: boolean; showServiceGroupSaveButton?: boolean; showServiceGroupsNav?: boolean; + showEnablementCallout?: boolean; selectedNavButton?: 'serviceGroups' | 'allServices'; } & KibanaPageTemplateProps & Pick) { @@ -126,6 +129,7 @@ export function ApmMainTemplate({ const pageHeaderTitle = ( {pageHeader?.pageTitle ?? pageTitle} + @@ -152,10 +156,14 @@ export function ApmMainTemplate({ rightSideItems, ...pageHeader, pageTitle: pageHeaderTitle, - children: - showServiceGroupsNav && selectedNavButton ? ( - - ) : null, + children: ( + + {showEnablementCallout && selectedNavButton === 'allServices' && } + {showServiceGroupsNav && selectedNavButton && ( + + )} + + ), }} {...pageTemplateProps} > diff --git a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/service_group_template.tsx b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/service_group_template.tsx index b34de178192d2..1f5074f6b0c78 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/routing/templates/service_group_template.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/routing/templates/service_group_template.tsx @@ -137,6 +137,7 @@ export function ServiceGroupTemplate({ environmentFilter={environmentFilter} showServiceGroupSaveButton={!isAllServices} showServiceGroupsNav={isAllServices} + showEnablementCallout selectedNavButton={isAllServices ? 'allServices' : 'serviceGroups'} {...pageTemplateProps} > diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/feedback_modal.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/feedback_modal.tsx new file mode 100644 index 0000000000000..e44dc8ae3a72b --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/feedback_modal.tsx @@ -0,0 +1,106 @@ +/* + * 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 React, { useContext } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + useEuiTheme, + EuiButtonEmpty, + EuiConfirmModal, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { getSurveyFeedbackURL } from '@kbn/observability-shared-plugin/public'; +import { KibanaEnvironmentContext } from '../../../context/kibana_environment_context/kibana_environment_context'; +import { getPathForFeedback } from '../../../utils/get_path_for_feedback'; + +export function FeedbackModal({ + isFeedbackModalVisible = false, + onClose, +}: { + isFeedbackModalVisible?: boolean; + onClose: () => void; +}) { + const kibanaEnvironment = useContext(KibanaEnvironmentContext); + const { kibanaVersion, isCloudEnv, isServerlessEnv } = kibanaEnvironment; + const sanitizedPath = getPathForFeedback(window.location.pathname); + const { euiTheme } = useEuiTheme(); + + return ( + <> + {isFeedbackModalVisible && ( + + {i18n.translate('xpack.apm.eemFeedback.button.openSurvey', { + defaultMessage: 'Tell us what you think!', + })} + + } + defaultFocusedButton="confirm" + > + + + + + + + +

    + {i18n.translate('xpack.apm.eemFeedback.title', { + defaultMessage: 'Let us know what you think!', + })} +

    +
    +
    +
    +
    + + + +

    + {i18n.translate('xpack.apm.feedbackModal.body.thanks', { + defaultMessage: + "Thank you for trying our new experience. We'll be continuing to improve on this so please come back often.", + })} +

    +
    +
    +
    + )} + + ); +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/index.tsx new file mode 100644 index 0000000000000..e305743243a4b --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/index.tsx @@ -0,0 +1,169 @@ +/* + * 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 React, { useState } from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ERROR_USER_NOT_AUTHORIZED } from '@kbn/entityManager-plugin/public'; +import useToggle from 'react-use/lib/useToggle'; +import { + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiLoadingSpinner, + EuiPopover, + EuiPopoverFooter, + EuiSkeletonText, + EuiText, + EuiTextColor, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { TechnicalPreviewBadge } from '../technical_preview_badge'; +import { ApmPluginStartDeps } from '../../../plugin'; +import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context'; +import { FeedbackModal } from './feedback_modal'; +import { UnauthorisedModal } from './unauthorized_modal'; + +export function EntityEnablement() { + const [isFeedbackModalVisible, setsIsFeedbackModalVisible] = useState(false); + const [isUnauthorizedModalVisible, setsIsUnauthorizedModalVisible] = useState(false); + + const { + services: { entityManager }, + } = useKibana(); + + const { isEntityManagerEnabled, isEnablementPending, refetch } = + useEntityManagerEnablementContext(); + + const [isPopoverOpen, togglePopover] = useToggle(false); + const [isLoading, setIsLoading] = useToggle(false); + + const handleRestoreView = async () => { + setIsLoading(true); + try { + const response = await entityManager.entityClient.disableManagedEntityDiscovery(); + if (response.success) { + setIsLoading(false); + setsIsFeedbackModalVisible(true); + } + } catch (error) { + setIsLoading(false); + setsIsFeedbackModalVisible(true); + console.error(error); + } + }; + + const handleEnableblement = async () => { + setIsLoading(true); + try { + const response = await entityManager.entityClient.enableManagedEntityDiscovery(); + if (response.success) { + setIsLoading(false); + refetch(); + } + + if (response.reason === ERROR_USER_NOT_AUTHORIZED) { + setIsLoading(false); + setsIsUnauthorizedModalVisible(true); + } + } catch (error) { + setIsLoading(false); + console.error(error); + } + }; + + const handdleOnCloseFeedback = () => { + setsIsFeedbackModalVisible(false); + refetch(); + }; + + return isEnablementPending ? ( + + + + ) : ( + + + {isLoading ? : } + + + + {isEntityManagerEnabled + ? i18n.translate('xpack.apm.eemEnablement.enabled.', { + defaultMessage: 'Viewing our new experience', + }) + : i18n.translate('xpack.apm.eemEnablement.tryItButton.', { + defaultMessage: 'Try our new experience!', + })} + + + + + } + isOpen={isPopoverOpen} + closePopover={togglePopover} + anchorPosition="downLeft" + > +
    + +

    + {i18n.translate('xpack.apm.entityEnablement.content', { + defaultMessage: + 'Our new experience combines both APM-instrumented services with services detected from logs in a single service inventory.', + })} +

    +
    +
    + + + + {i18n.translate('xpack.apm.entityEnablement.footer', { + defaultMessage: 'Learn more', + })} + + + +
    +
    + {isEntityManagerEnabled && ( + + + {i18n.translate('xpack.apm.eemEnablement.restoveClassicView.', { + defaultMessage: 'Restore classic view', + })} + + + )} + + setsIsUnauthorizedModalVisible(false)} + /> +
    + ); +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/unauthorized_modal.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/unauthorized_modal.tsx new file mode 100644 index 0000000000000..f728790f8c50f --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/entity_enablement/unauthorized_modal.tsx @@ -0,0 +1,106 @@ +/* + * 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. + */ + +/* +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButton, + EuiConfirmModal, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiImage, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { useKibanaUrl } from '../../../hooks/use_kibana_url'; + +export function UnauthorisedModal({ + isUnauthorizedModalVisible = false, + onClose, +}: { + isUnauthorizedModalVisible?: boolean; + onClose: () => void; +}) { + const servicesInventory = useKibanaUrl('/plugins/apm/assets/services_inventory.png'); + + return ( + <> + {isUnauthorizedModalVisible && ( + + {i18n.translate('xpack.apm.unauthorised.button.openSurvey', { + defaultMessage: 'OK', + })} + + } + defaultFocusedButton="confirm" + > + + + + + + + +

    + {i18n.translate('xpack.apm.unauthorised.title', { + defaultMessage: 'This feature is turned off', + })} +

    +
    +
    +
    +
    + + + +

    + {i18n.translate('xpack.apm.unauthorised.body', { + defaultMessage: + 'To see services detected from logs and APM-instrumented services in our new service inventory, please ask an administrator to visit this page and try our new experience. ', + })} +

    +
    +
    + + + + +
    + )} + + ); +} diff --git a/x-pack/plugins/observability_solution/apm/public/context/entity_manager_context/entity_manager_context.tsx b/x-pack/plugins/observability_solution/apm/public/context/entity_manager_context/entity_manager_context.tsx new file mode 100644 index 0000000000000..9fd41de01d8fe --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/context/entity_manager_context/entity_manager_context.tsx @@ -0,0 +1,40 @@ +/* + * 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 React, { createContext } from 'react'; +import { ENTITY_FETCH_STATUS, useEntityManager } from '../../hooks/use_entity_manager'; + +export interface EntityManagerEnablementContextValue { + isEntityManagerEnabled: boolean; + entityManagerEnablementStatus: ENTITY_FETCH_STATUS; + isEnablementPending: boolean; + refetch: () => void; +} + +export const EntityManagerEnablementContext = createContext( + {} as EntityManagerEnablementContextValue +); + +export function EntityManagerEnablementContextProvider({ + children, +}: { + children: React.ReactChild; +}) { + const { isEnabled, status, refetch } = useEntityManager(); + + return ( + + {children} + + ); +} diff --git a/x-pack/plugins/observability_solution/apm/public/context/entity_manager_context/use_entity_manager_enablement_context.ts b/x-pack/plugins/observability_solution/apm/public/context/entity_manager_context/use_entity_manager_enablement_context.ts new file mode 100644 index 0000000000000..83942af2e7715 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/context/entity_manager_context/use_entity_manager_enablement_context.ts @@ -0,0 +1,13 @@ +/* + * 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 { useContext } from 'react'; +import { EntityManagerEnablementContext } from './entity_manager_context'; + +export function useEntityManagerEnablementContext() { + return useContext(EntityManagerEnablementContext); +} diff --git a/x-pack/plugins/observability_solution/apm/public/hooks/use_entity_manager.ts b/x-pack/plugins/observability_solution/apm/public/hooks/use_entity_manager.ts new file mode 100644 index 0000000000000..554d58fe4cf10 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/hooks/use_entity_manager.ts @@ -0,0 +1,55 @@ +/* + * 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 { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useEffect, useMemo, useState } from 'react'; +import { ApmPluginStartDeps } from '../plugin'; + +export enum ENTITY_FETCH_STATUS { + LOADING = 'loading', + SUCCESS = 'success', + FAILURE = 'failure', + NOT_INITIATED = 'not_initiated', +} + +export function useEntityManager() { + const { + services: { entityManager }, + } = useKibana(); + const [counter, setCounter] = useState(0); + const [result, setResult] = useState({ + isEnabled: false, + status: ENTITY_FETCH_STATUS.NOT_INITIATED, + }); + + useEffect(() => { + async function isManagedEntityDiscoveryEnabled() { + setResult({ isEnabled: false, status: ENTITY_FETCH_STATUS.LOADING }); + + try { + const response = await entityManager.entityClient.isManagedEntityDiscoveryEnabled(); + setResult({ isEnabled: response?.enabled, status: ENTITY_FETCH_STATUS.SUCCESS }); + } catch (err) { + setResult({ isEnabled: false, status: ENTITY_FETCH_STATUS.FAILURE }); + + console.error(err); + } + } + + isManagedEntityDiscoveryEnabled(); + }, [entityManager, counter]); + + return useMemo(() => { + return { + ...result, + refetch: () => { + // this will invalidate the deps to `useEffect` and will result in a new request + setCounter((count) => count + 1); + }, + }; + }, [result]); +} diff --git a/x-pack/plugins/observability_solution/apm/public/plugin.ts b/x-pack/plugins/observability_solution/apm/public/plugin.ts index 087718436afa8..70a2176fdc595 100644 --- a/x-pack/plugins/observability_solution/apm/public/plugin.ts +++ b/x-pack/plugins/observability_solution/apm/public/plugin.ts @@ -19,6 +19,7 @@ import { PluginInitializerContext, SecurityServiceStart, } from '@kbn/core/public'; +import { EntityManagerPublicPluginSetup } from '@kbn/entityManager-plugin/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public'; @@ -105,6 +106,7 @@ export interface ApmPluginSetupDeps { uiActions: UiActionsSetup; profiling?: ProfilingPluginSetup; cloud?: CloudSetup; + entityManager: EntityManagerPublicPluginSetup; } export interface ApmServices { @@ -140,6 +142,7 @@ export interface ApmPluginStartDeps { dashboard: DashboardStart; metricsDataAccess: MetricsDataPluginStart; uiSettings: IUiSettingsClient; + entityManager: EntityManagerPublicPluginSetup; } const servicesTitle = i18n.translate('xpack.apm.navigation.servicesTitle', { diff --git a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts index 045d8bc251818..8b31141601d92 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts @@ -14,7 +14,7 @@ import { } from '@elastic/elasticsearch/lib/api/types'; import { withApmSpan } from '../../../../utils/with_apm_span'; -const ENTITIES_INDEX_NAME = '.entities-observability.latest-*'; +const ENTITIES_INDEX_NAME = '.entities.v1.latest.builtin_services*'; export function cancelEsRequestOnAbort>( promise: T, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts index 895129c33bdab..52c8e6983384c 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts @@ -4,12 +4,24 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import Boom from '@hapi/boom'; +import { toNumberRt, jsonRt } from '@kbn/io-ts-utils'; import * as t from 'io-ts'; +import { offsetRt } from '../../../../common/comparison_rt'; +import { getApmEventClient } from '../../../lib/helpers/get_apm_event_client'; +import { getRandomSampler } from '../../../lib/helpers/get_random_sampler'; import { EntityServiceListItem } from '../../../../common/entities/types'; import { environmentQuery } from '../../../../common/utils/environment_query'; import { createEntitiesESClient } from '../../../lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients'; import { createApmServerRoute } from '../../apm_routes/create_apm_server_route'; -import { environmentRt, kueryRt, rangeRt } from '../../default_api_types'; +import { + environmentRt, + kueryRt, + probabilityRt, + rangeRt, + serviceTransactionDataSourceRt, +} from '../../default_api_types'; +import { getServiceTransactionDetailedStatsPeriods } from '../../services/get_services_detailed_statistics/get_service_transaction_detailed_statistics'; import { getServiceEntities } from './get_service_entities'; export interface EntityServicesResponse { @@ -46,6 +58,99 @@ const servicesEntitiesRoute = createApmServerRoute({ }, }); +const servicesEntitiesDetailedStatisticsRoute = createApmServerRoute({ + endpoint: 'POST /internal/apm/entities/services/detailed_statistics', + params: t.type({ + query: t.intersection([ + environmentRt, + kueryRt, + rangeRt, + t.intersection([offsetRt, probabilityRt, serviceTransactionDataSourceRt]), + t.type({ + bucketSizeInSeconds: toNumberRt, + }), + ]), + body: t.type({ serviceNames: jsonRt.pipe(t.array(t.string)) }), + }), + options: { tags: ['access:apm'] }, + handler: async (resources) => { + const { + context, + params, + request, + plugins: { security, logsDataAccess }, + } = resources; + + const [coreContext, logsDataAccessStart] = await Promise.all([ + context.core, + logsDataAccess.start(), + ]); + + const { + environment, + kuery, + offset, + start, + end, + probability, + documentType, + rollupInterval, + bucketSizeInSeconds, + } = params.query; + + const { serviceNames } = params.body; + + if (!serviceNames.length) { + throw Boom.badRequest(`serviceNames cannot be empty`); + } + + const [apmEventClient, randomSampler] = await Promise.all([ + getApmEventClient(resources), + getRandomSampler({ security, request, probability }), + ]); + + const logsParams = { + esClient: coreContext.elasticsearch.client.asCurrentUser, + identifyingMetadata: 'service.name', + timeFrom: start, + timeTo: end, + kuery, + serviceEnvironmentQuery: environmentQuery(environment), + serviceNames, + }; + + const [ + currentPeriodLogsRateTimeseries, + currentPeriodLogsErrorRateTimeseries, + apmServiceTransactionDetailedStatsPeriods, + ] = await Promise.all([ + logsDataAccessStart.services.getLogsRateTimeseries(logsParams), + logsDataAccessStart.services.getLogsErrorRateTimeseries(logsParams), + getServiceTransactionDetailedStatsPeriods({ + environment, + kuery, + apmEventClient, + documentType, + rollupInterval, + bucketSizeInSeconds, + offset, + serviceNames, + start, + end, + randomSampler, + }), + ]); + + return { + currentPeriod: { + apm: { ...apmServiceTransactionDetailedStatsPeriods.currentPeriod }, + logErrorRate: { ...currentPeriodLogsErrorRateTimeseries }, + logRate: { ...currentPeriodLogsRateTimeseries }, + }, + }; + }, +}); + const serviceLogRateTimeseriesRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries', params: t.type({ @@ -65,8 +170,8 @@ const serviceLogRateTimeseriesRoute = createApmServerRoute({ const { serviceName } = params.path; const { start, end, kuery, environment } = params.query; - const curentPeriodlogsRateTimeseries = await logsDataAccessStart.services.getLogsRateTimeseries( - { + const currentPeriodLogsRateTimeseries = + await logsDataAccessStart.services.getLogsRateTimeseries({ esClient: coreContext.elasticsearch.client.asCurrentUser, identifyingMetadata: 'service.name', timeFrom: start, @@ -74,10 +179,9 @@ const serviceLogRateTimeseriesRoute = createApmServerRoute({ kuery, serviceEnvironmentQuery: environmentQuery(environment), serviceNames: [serviceName], - } - ); + }); - return { currentPeriod: curentPeriodlogsRateTimeseries }; + return { currentPeriod: currentPeriodLogsRateTimeseries }; }, }); @@ -118,4 +222,5 @@ export const servicesEntitiesRoutesRepository = { ...servicesEntitiesRoute, ...serviceLogRateTimeseriesRoute, ...serviceLogErrorRateTimeseriesRoute, + ...servicesEntitiesDetailedStatisticsRoute, }; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/historical_data/route.ts b/x-pack/plugins/observability_solution/apm/server/routes/historical_data/route.ts index 97f8f8c253033..c3513417cc565 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/historical_data/route.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/historical_data/route.ts @@ -5,12 +5,10 @@ * 2.0. */ -import { apmEnableMultiSignal } from '@kbn/observability-plugin/common'; import { createEntitiesESClient } from '../../lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; import { hasHistoricalAgentData } from './has_historical_agent_data'; -import { hasEntitiesData } from './has_historical_entities_data'; const hasDataRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/has_data', @@ -19,24 +17,14 @@ const hasDataRoute = createApmServerRoute({ const { context, request } = resources; const coreContext = await context.core; - const { - uiSettings: { client: uiSettingsClient }, - } = coreContext; - - const [apmEventClient, entitiesESClient, isApmEnableMultiSignal] = await Promise.all([ + const [apmEventClient] = await Promise.all([ getApmEventClient(resources), createEntitiesESClient({ request, esClient: coreContext.elasticsearch.client.asCurrentUser, }), - uiSettingsClient.get(apmEnableMultiSignal), ]); - if (isApmEnableMultiSignal) { - const hasData = await hasEntitiesData(entitiesESClient); - return { hasData }; - } - const hasData = await hasHistoricalAgentData(apmEventClient); return { hasData }; }, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts index 1ef359016c4d9..fd425d62fcb38 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/get_services_detailed_statistics/get_service_transaction_detailed_statistics.ts @@ -24,7 +24,7 @@ import { import { withApmSpan } from '../../../utils/with_apm_span'; import { maybe } from '../../../../common/utils/maybe'; -interface ServiceTransactionDetailedStat { +export interface ServiceTransactionDetailedStat { serviceName: string; latency: Array<{ x: number; y: number | null }>; transactionErrorRate?: Array<{ x: number; y: number }>; diff --git a/x-pack/plugins/observability_solution/apm/tsconfig.json b/x-pack/plugins/observability_solution/apm/tsconfig.json index 375283b72f6b6..fc7fccbded5dd 100644 --- a/x-pack/plugins/observability_solution/apm/tsconfig.json +++ b/x-pack/plugins/observability_solution/apm/tsconfig.json @@ -121,7 +121,8 @@ "@kbn/react-kibana-context-render", "@kbn/react-kibana-context-theme", "@kbn/test-jest-helpers", - "@kbn/security-plugin-types-common" + "@kbn/security-plugin-types-common", + "@kbn/entityManager-plugin" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/data_stream_stat.ts b/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/data_stream_stat.ts index 5968c369732de..164a43c625fb1 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/data_stream_stat.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/common/data_streams_stats/data_stream_stat.ts @@ -17,7 +17,7 @@ export class DataStreamStat { type: DataStreamType; name: DataStreamStatType['name']; namespace: string; - title: string; + title?: string; size?: DataStreamStatType['size']; // total datastream size sizeBytes?: DataStreamStatType['sizeBytes']; // total datastream size lastActivity?: DataStreamStatType['lastActivity']; diff --git a/x-pack/plugins/observability_solution/dataset_quality/common/types/common.ts b/x-pack/plugins/observability_solution/dataset_quality/common/types/common.ts index ca5c4632ec0d3..82d7b64e25f63 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/common/types/common.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/common/types/common.ts @@ -22,6 +22,5 @@ export interface BasicDataStream { name: DataStreamStatType['name']; rawName: string; namespace: string; - title: string; integration?: Integration; } diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/flyout.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/flyout.tsx index 7411067a7317f..7dc455b280444 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/flyout.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/flyout.tsx @@ -43,15 +43,16 @@ export default function Flyout({ dataset, closeFlyout }: FlyoutProps) { integration, } = useDatasetQualityFlyout(); - const titleAndLinkDetails: BasicDataStream = { + const linkDetails: BasicDataStream = { name: dataset.name, rawName: dataset.rawName, integration: integration?.integrationDetails, type: dataset.type, namespace: dataset.namespace, - title: integration?.integrationDetails?.datasets?.[dataset.name] ?? dataset.name, }; + const title = integration?.integrationDetails?.datasets?.[dataset.name] ?? dataset.name; + const { startTracking } = useDatasetDetailsTelemetry(); useEffect(() => { @@ -70,8 +71,9 @@ export default function Flyout({ dataset, closeFlyout }: FlyoutProps) { ) : ( <>
    diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/header.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/header.tsx index 5fc66f79b79ba..10d0c96a057bd 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/header.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/components/flyout/header.tsx @@ -27,17 +27,19 @@ import { IntegrationIcon } from '../common'; import { BasicDataStream } from '../../../common/types'; export function Header({ - titleAndLinkDetails, + linkDetails, loading, + title, }: { - titleAndLinkDetails: BasicDataStream; + linkDetails: BasicDataStream; loading: boolean; + title: string; }) { - const { integration, title } = titleAndLinkDetails; + const { integration } = linkDetails; const euiShadow = useEuiShadow('s'); const { euiTheme } = useEuiTheme(); const redirectLinkProps = useRedirectLink({ - dataStreamStat: titleAndLinkDetails, + dataStreamStat: linkDetails, telemetry: { page: 'details', navigationSource: NavigationSource.Header, diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.tsx index 03da29c7e04ea..9d459f8227a40 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.tsx @@ -14,7 +14,6 @@ import { useEuiTheme } from '@elastic/eui'; import { type DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { useDatasetQualityContext } from '../components/dataset_quality/context'; import { DEFAULT_LOGS_DATA_VIEW } from '../../common/constants'; -import { indexNameToDataStreamParts } from '../../common/utils'; import { getLensAttributes } from '../components/flyout/degraded_docs_trend/lens_attributes'; import { useCreateDataView } from './use_create_dataview'; import { useRedirectLink } from './use_redirect_link'; @@ -193,7 +192,7 @@ export const useDegradedDocsChart = ({ dataStream }: DegradedDocsChartDeps) => { }; function getDataViewIndexPattern(dataStream: string | undefined) { - return dataStream ? `${indexNameToDataStreamParts(dataStream).type}-*-*` : DEFAULT_LOGS_DATA_VIEW; + return dataStream ?? DEFAULT_LOGS_DATA_VIEW; } function getDataViewField(dataView: DataView | undefined, fieldName: string | undefined) { diff --git a/x-pack/plugins/observability_solution/entity_manager/public/index.ts b/x-pack/plugins/observability_solution/entity_manager/public/index.ts index e17edb959595d..e8b77529c2eac 100644 --- a/x-pack/plugins/observability_solution/entity_manager/public/index.ts +++ b/x-pack/plugins/observability_solution/entity_manager/public/index.ts @@ -18,3 +18,12 @@ export const plugin: PluginInitializer< export type { EntityManagerPublicPluginSetup, EntityManagerPublicPluginStart }; export type EntityManagerAppId = 'entityManager'; + +export { + ERROR_API_KEY_NOT_FOUND, + ERROR_API_KEY_NOT_VALID, + ERROR_USER_NOT_AUTHORIZED, + ERROR_API_KEY_SERVICE_DISABLED, + ERROR_PARTIAL_BUILTIN_INSTALLATION, + ERROR_DEFINITION_STOPPED, +} from '../common/errors'; diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts index 02c69771ed24f..29d501de8312f 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts @@ -10,6 +10,7 @@ import { BUILT_IN_ID_PREFIX } from './constants'; export const builtInServicesEntityDefinition: EntityDefinition = entityDefinitionSchema.parse({ id: `${BUILT_IN_ID_PREFIX}services`, + version: '0.1.0', name: 'Services from logs', type: 'service', managed: true, diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/create_and_install_ingest_pipeline.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/create_and_install_ingest_pipeline.ts index ce8515a5e31db..23feb84fa5336 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/create_and_install_ingest_pipeline.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/create_and_install_ingest_pipeline.ts @@ -28,6 +28,9 @@ export async function createAndInstallHistoryIngestPipeline( esClient.ingest.putPipeline({ id: historyId, processors: historyProcessors, + _meta: { + definitionVersion: definition.version, + }, }), { logger } ); @@ -51,6 +54,9 @@ export async function createAndInstallLatestIngestPipeline( esClient.ingest.putPipeline({ id: latestId, processors: latestProcessors, + _meta: { + definitionVersion: definition.version, + }, }), { logger } ); diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/helpers/fixtures/entity_definition.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/helpers/fixtures/entity_definition.ts index b0d47a9044d4a..137560df13385 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/helpers/fixtures/entity_definition.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/helpers/fixtures/entity_definition.ts @@ -8,6 +8,7 @@ import { entityDefinitionSchema } from '@kbn/entities-schema'; export const entityDefinition = entityDefinitionSchema.parse({ id: 'admin-console-services', + version: '999.999.999', name: 'Services for Admin Console', type: 'service', indexPatterns: ['kbn-data-forge-fake_stack.*'], diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_history_processors.test.ts.snap b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_history_processors.test.ts.snap index c646694ffef5e..eb74937a7e8c8 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_history_processors.test.ts.snap +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_history_processors.test.ts.snap @@ -20,6 +20,18 @@ Array [ "value": "admin-console-services", }, }, + Object { + "set": Object { + "field": "entity.definitionVersion", + "value": "999.999.999", + }, + }, + Object { + "set": Object { + "field": "entity.schemaVersion", + "value": "v1", + }, + }, Object { "set": Object { "field": "entity.displayName", diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_latest_processors.test.ts.snap b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_latest_processors.test.ts.snap index 33d8504d937b2..c66bd8337da47 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_latest_processors.test.ts.snap +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_latest_processors.test.ts.snap @@ -20,6 +20,18 @@ Array [ "value": "admin-console-services", }, }, + Object { + "set": Object { + "field": "entity.definitionVersion", + "value": "999.999.999", + }, + }, + Object { + "set": Object { + "field": "entity.schemaVersion", + "value": "v1", + }, + }, Object { "script": Object { "source": "if (ctx.entity?.metadata?.tags.data != null) { diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_history_processors.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_history_processors.ts index eea33d9adda79..b273be780910b 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_history_processors.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_history_processors.ts @@ -6,6 +6,7 @@ */ import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_SCHEMA_VERSION_V1 } from '../../../../common/constants_entities'; import { generateHistoryIndexName } from '../helpers/generate_component_id'; function createIdTemplate(definition: EntityDefinition) { @@ -62,6 +63,18 @@ export function generateHistoryProcessors(definition: EntityDefinition) { value: definition.id, }, }, + { + set: { + field: 'entity.definitionVersion', + value: definition.version, + }, + }, + { + set: { + field: 'entity.schemaVersion', + value: ENTITY_SCHEMA_VERSION_V1, + }, + }, { set: { field: 'entity.displayName', diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_latest_processors.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_latest_processors.ts index 1574659723601..056fb043ab05c 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_latest_processors.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/ingest_pipeline/generate_latest_processors.ts @@ -6,6 +6,7 @@ */ import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_SCHEMA_VERSION_V1 } from '../../../../common/constants_entities'; import { generateLatestIndexName } from '../helpers/generate_component_id'; function mapDestinationToPainless(destination: string) { @@ -56,6 +57,18 @@ export function generateLatestProcessors(definition: EntityDefinition) { value: definition.id, }, }, + { + set: { + field: 'entity.definitionVersion', + value: definition.version, + }, + }, + { + set: { + field: 'entity.schemaVersion', + value: ENTITY_SCHEMA_VERSION_V1, + }, + }, ...(definition.staticFields != null ? Object.keys(definition.staticFields).map((field) => ({ set: { field, value: definition.staticFields![field] }, diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.test.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.test.ts index d246b3f4f5606..6e7080c6364b7 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.test.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.test.ts @@ -38,10 +38,16 @@ const assertHasCreatedDefinition = ( expect(esClient.ingest.putPipeline).toBeCalledWith({ id: generateHistoryIngestPipelineId(builtInServicesEntityDefinition), processors: expect.anything(), + _meta: { + definitionVersion: '0.1.0', + }, }); expect(esClient.ingest.putPipeline).toBeCalledWith({ id: generateLatestIngestPipelineId(builtInServicesEntityDefinition), processors: expect.anything(), + _meta: { + definitionVersion: '0.1.0', + }, }); expect(esClient.transform.putTransform).toBeCalledTimes(2); diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_history_transform.test.ts.snap b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_history_transform.test.ts.snap index bbea064c9b7ec..961f57045a5a2 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_history_transform.test.ts.snap +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_history_transform.test.ts.snap @@ -2,6 +2,9 @@ exports[`generateHistoryTransform(definition) should generate a valid latest transform 1`] = ` Object { + "_meta": Object { + "definitionVersion": "999.999.999", + }, "defer_validation": true, "dest": Object { "index": ".entities.v1.history.noop", diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_latest_transform.test.ts.snap b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_latest_transform.test.ts.snap index d9dff5aa54c2e..aa0b2c7e871d2 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_latest_transform.test.ts.snap +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/__snapshots__/generate_latest_transform.test.ts.snap @@ -2,6 +2,9 @@ exports[`generateLatestTransform(definition) should generate a valid latest transform 1`] = ` Object { + "_meta": Object { + "definitionVersion": "999.999.999", + }, "defer_validation": true, "dest": Object { "index": ".entities.v1.latest.noop", diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_history_transform.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_history_transform.ts index 2fd430f142b72..4467c2425976c 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_history_transform.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_history_transform.ts @@ -34,6 +34,9 @@ export function generateHistoryTransform( return { transform_id: generateHistoryTransformId(definition), + _meta: { + definitionVersion: definition.version, + }, defer_validation: true, source: { index: definition.indexPatterns, diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_latest_transform.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_latest_transform.ts index 9f19f083291f2..e52aa10e0820d 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_latest_transform.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/transform/generate_latest_transform.ts @@ -25,6 +25,9 @@ export function generateLatestTransform( ): TransformPutTransformRequest { return { transform_id: generateLatestTransformId(definition), + _meta: { + definitionVersion: definition.version, + }, defer_validation: true, source: { index: `${generateHistoryIndexName(definition)}.*`, diff --git a/x-pack/plugins/observability_solution/entity_manager/server/saved_objects/entity_definition.ts b/x-pack/plugins/observability_solution/entity_manager/server/saved_objects/entity_definition.ts index 5ddba63b7ad07..b4bf24580d632 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/saved_objects/entity_definition.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/saved_objects/entity_definition.ts @@ -18,6 +18,7 @@ export const entityDefinition: SavedObjectsType = { dynamic: false, properties: { id: { type: 'keyword' }, + version: { type: 'keyword' }, name: { type: 'text' }, description: { type: 'text' }, type: { type: 'keyword' }, @@ -37,4 +38,16 @@ export const entityDefinition: SavedObjectsType = { return `EntityDefinition: [${savedObject.attributes.name}]`; }, }, + modelVersions: { + '1': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + version: { type: 'keyword' }, + }, + }, + ], + }, + }, }; diff --git a/x-pack/plugins/observability_solution/entity_manager/server/templates/components/entity.ts b/x-pack/plugins/observability_solution/entity_manager/server/templates/components/entity.ts index 88c01b113c0e4..fd7e82749f069 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/templates/components/entity.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/templates/components/entity.ts @@ -41,6 +41,14 @@ export const entitiesEntityComponentTemplateConfig: ClusterPutComponentTemplateR ignore_above: 1024, type: 'keyword', }, + definitionVersion: { + ignore_above: 1024, + type: 'keyword', + }, + schemaVersion: { + ignore_above: 1024, + type: 'keyword', + }, lastSeenTimestamp: { type: 'date', }, diff --git a/x-pack/plugins/observability_solution/exploratory_view/kibana.jsonc b/x-pack/plugins/observability_solution/exploratory_view/kibana.jsonc index 9fadcd2d68ca3..4061de177e427 100644 --- a/x-pack/plugins/observability_solution/exploratory_view/kibana.jsonc +++ b/x-pack/plugins/observability_solution/exploratory_view/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/exploratory-view-plugin", - "owner": "@elastic/obs-ux-infra_services-team", + "owner": "@elastic/obs-ux-management-team", "plugin": { "id": "exploratoryView", "server": false, diff --git a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomalies_table/anomalies_table.tsx b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomalies_table/anomalies_table.tsx index b38af534df6e7..482d4c7294c5e 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomalies_table/anomalies_table.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomalies_table/anomalies_table.tsx @@ -221,9 +221,9 @@ export const AnomaliesTable = ({ }: Props) => { const [search, setSearch] = useState(''); const trackMetric = useUiTracker({ app: 'infra_metrics' }); - const [timeRange, setTimeRange] = useState<{ start: number; end: number }>({ - start: datemathToEpochMillis(dateRange.from) || 0, - end: datemathToEpochMillis(dateRange.to, 'up') || 0, + const [timeRange, setTimeRange] = useState<{ start: string; end: string }>({ + start: dateRange.from, + end: dateRange.to, }); const { sorting, setSorting } = useSorting({ field: 'startTime', @@ -256,8 +256,8 @@ export const AnomaliesTable = ({ ({ isInvalid, start: startChange, end: endChange }: OnTimeChangeProps) => { if (!isInvalid) { setTimeRange({ - start: datemathToEpochMillis(startChange)!, - end: datemathToEpochMillis(endChange, 'up')!, + start: startChange, + end: endChange, }); } }, @@ -265,14 +265,17 @@ export const AnomaliesTable = ({ ); const getTimeRange = useCallback(() => { - if (hideDatePicker) { - return { - start: datemathToEpochMillis(dateRange.from) || 0, - end: datemathToEpochMillis(dateRange.to, 'up') || 0, - }; - } else { - return timeRange; - } + const { start, end } = hideDatePicker + ? { + start: dateRange.from, + end: dateRange.to, + } + : timeRange; + + return { + start: datemathToEpochMillis(start) || 0, + end: datemathToEpochMillis(end, 'up') || 0, + }; }, [dateRange.from, dateRange.to, hideDatePicker, timeRange]); const anomalyParams = useMemo(() => { @@ -483,8 +486,8 @@ export const AnomaliesTable = ({ {!hideDatePicker && ( ({ +const getLogErrorsAggregation = () => ({ terms: { field: LOG_LEVEL, include: ['error', 'ERROR'], }, }); -type LogErrorsAggregation = ReturnType; +type LogErrorsAggregation = ReturnType; interface LogsErrorRateTimeseriesHistogram { timeseries: AggregationResultOf< { @@ -103,7 +103,7 @@ export function createGetLogErrorRateTimeseries() { }, }, aggs: { - logErrors: getLogErrorsAggegation(), + logErrors: getLogErrorsAggregation(), }, }, }, diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx index 24782cd2ab2bb..6610db470014b 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/dataset_quality_link.tsx @@ -12,7 +12,7 @@ import { BrowserUrlService } from '@kbn/share-plugin/public'; import { MatchedStateFromActor } from '@kbn/xstate-utils'; import { useActor } from '@xstate/react'; import React from 'react'; -import { DataQualityLocatorParams, DATA_QUALITY_LOCATOR_ID } from '@kbn/data-quality-plugin/common'; +import { DataQualityLocatorParams, DATA_QUALITY_LOCATOR_ID } from '@kbn/deeplinks-observability'; import { datasetQualityLinkTitle } from '../../common/translations'; import { ObservabilityLogsExplorerService, diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json b/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json index 446c237e257eb..26db8756fb9a1 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json @@ -49,7 +49,6 @@ "@kbn/es-query", "@kbn/core-analytics-browser", "@kbn/react-hooks", - "@kbn/data-quality-plugin", "@kbn/ebt", ], "exclude": [ diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/observability_onboarding_flow.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/observability_onboarding_flow.tsx index 33e472d841bbc..dd22b1735fc9f 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/observability_onboarding_flow.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/observability_onboarding_flow.tsx @@ -19,6 +19,7 @@ import { SystemLogsPanel } from './quickstart_flows/system_logs'; import { CustomLogsPanel } from './quickstart_flows/custom_logs'; import { OtelLogsPanel } from './quickstart_flows/otel_logs'; import { AutoDetectPanel } from './quickstart_flows/auto_detect'; +import { KubernetesPanel } from './quickstart_flows/kubernetes'; import { BackButton } from './shared/back_button'; const queryClient = new QueryClient(); @@ -66,6 +67,10 @@ export function ObservabilityOnboardingFlow() { + + + + diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.ts index 5bc63403365cc..03d7d0278f374 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.ts @@ -34,6 +34,7 @@ export function useCustomCardsForCategory( const { href: systemLogsUrl } = reactRouterNavigate(history, `/systemLogs/${location.search}`); const { href: customLogsUrl } = reactRouterNavigate(history, `/customLogs/${location.search}`); const { href: otelLogsUrl } = reactRouterNavigate(history, `/otel-logs/${location.search}`); + const { href: kubernetesUrl } = reactRouterNavigate(history, `/kubernetes/${location.search}`); const otelCard: VirtualCard = { id: 'otel-logs', @@ -112,7 +113,23 @@ export function useCustomCardsForCategory( ]; case 'infra': return [ - toFeaturedCard('kubernetes'), + { + id: 'kubernetes-quick-start', + type: 'virtual', + title: 'Kubernetes', + description: 'Collect logs and metrics from Kubernetes using minimal configuration', + name: 'kubernetes-quick-start', + categories: ['observability'], + icons: [ + { + type: 'svg', + src: http?.staticAssets.getPluginAssetHref('kubernetes.svg') ?? '', + }, + ], + url: kubernetesUrl, + version: '', + integration: '', + }, toFeaturedCard('docker'), isServerless ? toFeaturedCard('prometheus') : otelCard, { diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/utils.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/utils.ts index 52e9a0f09c807..e0e468b0d80db 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/utils.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/utils.ts @@ -7,7 +7,7 @@ import { IntegrationCardItem } from '@kbn/fleet-plugin/public'; -export const QUICKSTART_FLOWS = ['system-logs-virtual']; +export const QUICKSTART_FLOWS = ['system-logs-virtual', 'kubernetes-quick-start']; export const toCustomCard = (card: IntegrationCardItem) => ({ ...card, diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/build_kubectl_command.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/build_kubectl_command.ts new file mode 100644 index 0000000000000..59de30111649d --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/build_kubectl_command.ts @@ -0,0 +1,38 @@ +/* + * 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. + */ + +interface Params { + encodedApiKey: string; + onboardingId: string; + elasticsearchUrl: string; + elasticAgentVersion: string; +} + +const KUSTOMIZE_TEMPLATE_URL = + 'https://github.com/elastic/elastic-agent/deploy/kubernetes/elastic-agent-kustomize/default/elastic-agent-standalone'; + +export function buildKubectlCommand({ + encodedApiKey, + onboardingId, + elasticsearchUrl, + elasticAgentVersion, +}: Params) { + const escapedElasticsearchUrl = elasticsearchUrl.replace(/\//g, '\\/'); + + return ` + kubectl kustomize ${KUSTOMIZE_TEMPLATE_URL}\\?ref\\=v${elasticAgentVersion} + | sed -e 's/JUFQSV9LRVkl/${encodedApiKey}/g' + -e "s/%ES_HOST%/${escapedElasticsearchUrl}/g" + -e "s/%ONBOARDING_ID%/${onboardingId}/g" + -e "s/\\(docker.elastic.co\\/beats\\/elastic-agent\:\\).*$/\\1${elasticAgentVersion}/g" + -e "/{CA_TRUSTED}/c\\ " + | kubectl apply -f- + ` + .trim() + .replace(/\n/g, ' ') + .replace(/\s\s+/g, ' '); +} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/command_snippet.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/command_snippet.tsx new file mode 100644 index 0000000000000..be7857676427c --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/command_snippet.tsx @@ -0,0 +1,72 @@ +/* + * 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 React from 'react'; +import { EuiCodeBlock, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { buildKubectlCommand } from './build_kubectl_command'; +import { CopyToClipboardButton } from '../shared/copy_to_clipboard_button'; + +interface Props { + encodedApiKey: string; + onboardingId: string; + elasticsearchUrl: string; + elasticAgentVersion: string; +} + +export function CommandSnippet({ + encodedApiKey, + onboardingId, + elasticsearchUrl, + elasticAgentVersion, +}: Props) { + const command = buildKubectlCommand({ + encodedApiKey, + onboardingId, + elasticsearchUrl, + elasticAgentVersion, + }); + + return ( + <> + +

    + + {i18n.translate( + 'xpack.observability_onboarding.kubernetesPanel.scalingElasticAgentOnLinkLabel', + { defaultMessage: 'Scaling Elastic Agent on Kubernetes' } + )} + + ), + }} + /> +

    +
    + + + + + {command} + + + + + + + ); +} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/data_ingest_status.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/data_ingest_status.tsx new file mode 100644 index 0000000000000..3e91c20752bd4 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/data_ingest_status.tsx @@ -0,0 +1,182 @@ +/* + * 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 React, { useEffect, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { DASHBOARD_APP_LOCATOR } from '@kbn/deeplinks-analytics'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + StepsProgress, + useFlowProgressTelemetry, +} from '../../../hooks/use_flow_progress_telemetry'; +import { ObservabilityOnboardingContextValue } from '../../../plugin'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; +import { ProgressIndicator } from '../shared/progress_indicator'; + +interface Props { + onboardingId: string; +} + +const FETCH_INTERVAL = 2000; +const SHOW_TROUBLESHOOTING_DELAY = 120000; // 2 minutes +const CLUSTER_OVERVIEW_DASHBOARD_ID = 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c'; + +export function DataIngestStatus({ onboardingId }: Props) { + const { + services: { share, http }, + } = useKibana(); + const [progress, setProgress] = useState(undefined); + const [checkDataStartTime] = useState(Date.now()); + + const dashboardLocator = share.url.locators.get(DASHBOARD_APP_LOCATOR); + + const { data, status, refetch } = useFetcher( + (callApi) => { + return callApi('GET /internal/observability_onboarding/kubernetes/{onboardingId}/has-data', { + params: { path: { onboardingId } }, + }); + }, + [onboardingId] + ); + + useEffect(() => { + const pendingStatusList = [FETCH_STATUS.LOADING, FETCH_STATUS.NOT_INITIATED]; + + if (pendingStatusList.includes(status) || data?.hasData === true) { + return; + } + + const timeout = setTimeout(() => { + refetch(); + }, FETCH_INTERVAL); + + return () => clearTimeout(timeout); + }, [data?.hasData, refetch, status]); + + useEffect(() => { + if (data?.hasData === true) { + setProgress({ 'logs-ingest': { status: 'complete' } }); + } + }, [data?.hasData]); + + useFlowProgressTelemetry(progress, onboardingId); + + const isTroubleshootingVisible = + data?.hasData === false && Date.now() - checkDataStartTime > SHOW_TROUBLESHOOTING_DELAY; + + return ( + <> + + + {isTroubleshootingVisible && ( + <> + + + + {i18n.translate( + 'xpack.observability_onboarding.dataIngestStatus.troubleshootingLinkText', + { + defaultMessage: 'Open documentation', + } + )} + + ), + }} + /> + + + )} + + {data?.hasData === true && ( + <> + + + + + + + + + +

    + {i18n.translate( + 'xpack.observability_onboarding.kubernetesPanel.monitoringCluster', + { + defaultMessage: + 'Overview your Kubernetes cluster with this pre-made dashboard', + } + )} +

    +
    + + + {i18n.translate('xpack.observability_onboarding.kubernetesPanel.exploreDashboard', { + defaultMessage: 'Explore Kubernetes cluster', + })} + +
    +
    + + + + + + {i18n.translate( + 'xpack.observability_onboarding.dataIngestStatus.viewAllAssetsLinkText', + { + defaultMessage: 'View all assets', + } + )} + + ), + }} + /> + + + )} + + ); +} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/index.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/index.tsx new file mode 100644 index 0000000000000..58de3a72ca92f --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/kubernetes/index.tsx @@ -0,0 +1,73 @@ +/* + * 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 React, { useState } from 'react'; +import { + EuiPanel, + EuiSkeletonRectangle, + EuiSkeletonText, + EuiSpacer, + EuiSteps, + EuiStepStatus, +} from '@elastic/eui'; +import useEvent from 'react-use/lib/useEvent'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; +import { EmptyPrompt } from '../shared/empty_prompt'; +import { CommandSnippet } from './command_snippet'; +import { DataIngestStatus } from './data_ingest_status'; + +export const KubernetesPanel: React.FC = () => { + const [windowLostFocus, setWindowLostFocus] = useState(false); + const { data, status, error, refetch } = useFetcher((callApi) => { + return callApi('POST /internal/observability_onboarding/kubernetes/flow'); + }, []); + + useEvent('blur', () => setWindowLostFocus(true), window); + + if (error !== undefined) { + return ; + } + + const isMonitoringStepActive = + status === FETCH_STATUS.SUCCESS && data !== undefined && windowLostFocus; + + const steps = [ + { + title: 'Install Elastic Agent on your host', + children: ( + <> + {status !== FETCH_STATUS.SUCCESS && ( + <> + + + + + )} + {status === FETCH_STATUS.SUCCESS && data !== undefined && ( + + )} + + ), + }, + { + title: 'Monitor your Kubernetes cluster', + status: (isMonitoringStepActive ? 'current' : 'incomplete') as EuiStepStatus, + children: isMonitoringStepActive && , + }, + ]; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/kubernetes.svg b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/kubernetes.svg new file mode 100644 index 0000000000000..7f3e86f5482b9 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/kubernetes.svg @@ -0,0 +1 @@ + diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/hooks/use_flow_progress_telemetry.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/hooks/use_flow_progress_telemetry.ts index 95d93ad6af985..a580055f8e3c7 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/hooks/use_flow_progress_telemetry.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/hooks/use_flow_progress_telemetry.ts @@ -11,9 +11,9 @@ import { type LogsFlowProgressStepId } from '../../common/logs_flow_progress_ste import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../common/telemetry_events'; import { useKibana } from './use_kibana'; -type EuiStepStatus = EuiStepsProps['steps'][number]['status']; +export type EuiStepStatus = EuiStepsProps['steps'][number]['status']; -type StepsProgress = Partial< +export type StepsProgress = Partial< Record >; diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/create_install_api_key.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_install_api_key.ts similarity index 96% rename from x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/create_install_api_key.ts rename to x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_install_api_key.ts index d97dd6ac6580c..eddc5e10b5c65 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/create_install_api_key.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_install_api_key.ts @@ -13,7 +13,7 @@ import type { CreateAPIKeyParams } from '@kbn/security-plugin/server'; */ export function createInstallApiKey(name: string): CreateAPIKeyParams { return { - name: `onboarding_install_${name}`, + name, expiration: '1h', // This API key is only used for initial setup and should be short lived metadata: { managed: true, diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/create_shipper_api_key.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_shipper_api_key.ts similarity index 93% rename from x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/create_shipper_api_key.ts rename to x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_shipper_api_key.ts index 70a3bf344fee6..942ebdbbd07cd 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/create_shipper_api_key.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/create_shipper_api_key.ts @@ -12,7 +12,7 @@ export function createShipperApiKey(esClient: ElasticsearchClient, name: string) // Based on https://www.elastic.co/guide/en/fleet/master/grant-access-to-elasticsearch.html#create-api-key-standalone-agent return esClient.security.createApiKey({ body: { - name: `standalone_agent_logs_onboarding_${name}`, + name, metadata: { managed: true, application: 'logs', diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/has_log_monitoring_privileges.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/has_log_monitoring_privileges.ts similarity index 100% rename from x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/has_log_monitoring_privileges.ts rename to x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/has_log_monitoring_privileges.ts diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/monitoring_config.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/monitoring_config.ts similarity index 100% rename from x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/api_key/monitoring_config.ts rename to x-pack/plugins/observability_solution/observability_onboarding/server/lib/api_key/monitoring_config.ts diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts index c58a5ef257fbb..80f0159f66594 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts @@ -20,12 +20,12 @@ import { ObservabilityOnboardingFlow } from '../../saved_objects/observability_o import { createObservabilityOnboardingServerRoute } from '../create_observability_onboarding_server_route'; import { getHasLogs } from './get_has_logs'; import { getKibanaUrl } from '../../lib/get_fallback_urls'; -import { hasLogMonitoringPrivileges } from '../logs/api_key/has_log_monitoring_privileges'; -import { createShipperApiKey } from '../logs/api_key/create_shipper_api_key'; -import { createInstallApiKey } from '../logs/api_key/create_install_api_key'; import { getAgentVersion } from '../../lib/get_agent_version'; import { getFallbackESUrl } from '../../lib/get_fallback_urls'; import { ElasticAgentStepPayload, InstalledIntegration, StepProgressPayloadRT } from '../types'; +import { createShipperApiKey } from '../../lib/api_key/create_shipper_api_key'; +import { createInstallApiKey } from '../../lib/api_key/create_install_api_key'; +import { hasLogMonitoringPrivileges } from '../../lib/api_key/has_log_monitoring_privileges'; const updateOnboardingFlowRoute = createObservabilityOnboardingServerRoute({ endpoint: 'PUT /internal/observability_onboarding/flow/{onboardingId}', @@ -229,8 +229,11 @@ const createFlowRoute = createObservabilityOnboardingServerRoute({ progress: {}, }, }), - createShipperApiKey(client.asCurrentUser, name), - securityPluginStart.authc.apiKeys.create(request, createInstallApiKey(name)), + createShipperApiKey(client.asCurrentUser, `onboarding_ingest_${name}`), + securityPluginStart.authc.apiKeys.create( + request, + createInstallApiKey(`onboarding_install_${name}`) + ), getAgentVersion(fleetPluginStart, kibanaVersion), ]); diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/index.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/index.ts index 2f9ce174cf040..e444f2266f18f 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/index.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/index.ts @@ -7,6 +7,7 @@ import type { EndpointOf, ServerRouteRepository } from '@kbn/server-route-repository'; import { elasticAgentRouteRepository } from './elastic_agent/route'; import { flowRouteRepository } from './flow/route'; +import { kubernetesOnboardingRouteRepository } from './kubernetes/route'; import { logsOnboardingRouteRepository } from './logs/route'; function getTypedObservabilityOnboardingServerRouteRepository() { @@ -14,6 +15,7 @@ function getTypedObservabilityOnboardingServerRouteRepository() { ...flowRouteRepository, ...logsOnboardingRouteRepository, ...elasticAgentRouteRepository, + ...kubernetesOnboardingRouteRepository, }; return repository; diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/kubernetes/route.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/kubernetes/route.ts new file mode 100644 index 0000000000000..efbea4a0bcd4e --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/kubernetes/route.ts @@ -0,0 +1,112 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import * as t from 'io-ts'; +import Boom from '@hapi/boom'; +import { termQuery } from '@kbn/observability-plugin/server'; +import type { estypes } from '@elastic/elasticsearch'; +import { getFallbackESUrl } from '../../lib/get_fallback_urls'; +import { createObservabilityOnboardingServerRoute } from '../create_observability_onboarding_server_route'; +import { hasLogMonitoringPrivileges } from '../../lib/api_key/has_log_monitoring_privileges'; +import { createShipperApiKey } from '../../lib/api_key/create_shipper_api_key'; +import { getAgentVersion } from '../../lib/get_agent_version'; + +export interface CreateKubernetesOnboardingFlowRouteResponse { + apiKeyEncoded: string; + onboardingId: string; + elasticsearchUrl: string; + elasticAgentVersion: string; +} + +export interface HasKubernetesDataRouteResponse { + hasData: boolean; +} + +const createKubernetesOnboardingFlowRoute = createObservabilityOnboardingServerRoute({ + endpoint: 'POST /internal/observability_onboarding/kubernetes/flow', + options: { tags: [] }, + async handler({ + context, + request, + plugins, + services, + kibanaVersion, + }): Promise { + const { + elasticsearch: { client }, + } = await context.core; + + const hasPrivileges = await hasLogMonitoringPrivileges(client.asCurrentUser); + + if (!hasPrivileges) { + throw Boom.forbidden( + "You don't have enough privileges to start a new onboarding flow. Contact your system administrator to grant you the required privileges." + ); + } + + const fleetPluginStart = await plugins.fleet.start(); + const packageClient = fleetPluginStart.packageService.asScoped(request); + + await packageClient.ensureInstalledPackage({ pkgName: 'kubernetes' }); + + const [{ encoded: apiKeyEncoded }, elasticAgentVersion] = await Promise.all([ + createShipperApiKey(client.asCurrentUser, 'kubernetes_onboarding'), + getAgentVersion(fleetPluginStart, kibanaVersion), + ]); + const elasticsearchUrlList = plugins.cloud?.setup?.elasticsearchUrl + ? [plugins.cloud?.setup?.elasticsearchUrl] + : await getFallbackESUrl(services.esLegacyConfigService); + + return { + onboardingId: uuidv4(), + apiKeyEncoded, + elasticsearchUrl: elasticsearchUrlList.length > 0 ? elasticsearchUrlList[0] : '', + elasticAgentVersion, + }; + }, +}); + +const hasKubernetesDataRoute = createObservabilityOnboardingServerRoute({ + endpoint: 'GET /internal/observability_onboarding/kubernetes/{onboardingId}/has-data', + params: t.type({ + path: t.type({ + onboardingId: t.string, + }), + }), + options: { tags: [] }, + async handler(resources): Promise { + const { onboardingId } = resources.params.path; + const { elasticsearch } = await resources.context.core; + + try { + const result = await elasticsearch.client.asCurrentUser.search({ + index: ['logs-*', 'metrics-*'], + ignore_unavailable: true, + size: 0, + terminate_after: 1, + query: { + bool: { + filter: termQuery('fields.onboarding_id', onboardingId), + }, + }, + }); + const { value } = result.hits.total as estypes.SearchTotalHits; + + return { + hasData: value > 0, + }; + } catch (error) { + throw Boom.internal(`Elasticsearch responses with an error. ${error.message}`); + } + }, +}); + +export const kubernetesOnboardingRouteRepository = { + ...createKubernetesOnboardingFlowRoute, + ...hasKubernetesDataRoute, +}; diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts index 524037c1c4222..6d17617e1a94e 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts @@ -10,10 +10,10 @@ import { createObservabilityOnboardingServerRoute } from '../create_observabilit import { getFallbackESUrl } from '../../lib/get_fallback_urls'; import { getKibanaUrl } from '../../lib/get_fallback_urls'; import { getAgentVersion } from '../../lib/get_agent_version'; -import { hasLogMonitoringPrivileges } from './api_key/has_log_monitoring_privileges'; import { saveObservabilityOnboardingFlow } from '../../lib/state'; -import { createShipperApiKey } from './api_key/create_shipper_api_key'; +import { createShipperApiKey } from '../../lib/api_key/create_shipper_api_key'; import { ObservabilityOnboardingFlow } from '../../saved_objects/observability_onboarding_status'; +import { hasLogMonitoringPrivileges } from '../../lib/api_key/has_log_monitoring_privileges'; const logMonitoringPrivilegesRoute = createObservabilityOnboardingServerRoute({ endpoint: 'GET /internal/observability_onboarding/logs/setup/privileges', @@ -115,7 +115,10 @@ const createFlowRoute = createObservabilityOnboardingServerRoute({ const { elasticsearch: { client }, } = await context.core; - const { encoded: apiKeyEncoded } = await createShipperApiKey(client.asCurrentUser, name); + const { encoded: apiKeyEncoded } = await createShipperApiKey( + client.asCurrentUser, + `standalone_agent_logs_onboarding_${name}` + ); const generatedState = type === 'systemLogs' ? { namespace: 'default' } : state; const savedObjectsClient = coreStart.savedObjects.getScopedClient(request); diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts index a7ef942d7ea0a..bf996b96e1958 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/saved_objects/observability_onboarding_status.ts @@ -23,10 +23,10 @@ export interface SystemLogsState { namespace: string; } -export type ObservabilityOnboardingType = 'logFiles' | 'systemLogs' | 'autoDetect'; - type ObservabilityOnboardingFlowState = LogFilesState | SystemLogsState | undefined; +type ObservabilityOnboardingType = 'logFiles' | 'systemLogs' | 'autoDetect' | 'kubernetes'; + export interface ObservabilityOnboardingFlow { type: ObservabilityOnboardingType; state: ObservabilityOnboardingFlowState; diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/test_helpers/create_observability_onboarding_users/authentication.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/test_helpers/create_observability_onboarding_users/authentication.ts index 95da640fa40ae..340f0cb615651 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/test_helpers/create_observability_onboarding_users/authentication.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/test_helpers/create_observability_onboarding_users/authentication.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { cluster, indices } from '../../routes/logs/api_key/monitoring_config'; +import { cluster, indices } from '../../lib/api_key/monitoring_config'; export enum ObservabilityOnboardingUsername { noAccessUser = 'no_access_user', diff --git a/x-pack/plugins/observability_solution/synthetics/kibana.jsonc b/x-pack/plugins/observability_solution/synthetics/kibana.jsonc index 90076811dc75b..3e0715e48abb1 100644 --- a/x-pack/plugins/observability_solution/synthetics/kibana.jsonc +++ b/x-pack/plugins/observability_solution/synthetics/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/synthetics-plugin", - "owner": "@elastic/obs-ux-infra_services-team", + "owner": "@elastic/obs-ux-management-team", "description": "This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions.", "plugin": { "id": "synthetics", diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx index 4c151880ff588..7ea35752fefac 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx @@ -15,6 +15,7 @@ import { EuiLoadingSpinner, EuiTitle, EuiCallOut, + EuiScreenReaderLive, useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -153,6 +154,13 @@ function getButtonContent({ journeyStarted, completedSteps, }: CheckGroupResult) { + const completedText = i18n.translate('xpack.synthetics.monitorManagement.stepCompleted', { + defaultMessage: '{stepCount, number} {stepCount, plural, one {step} other {steps}} completed', + values: { + stepCount: completedSteps ?? 0, + }, + }); + return (

    - - {i18n.translate('xpack.synthetics.monitorManagement.stepCompleted', { - defaultMessage: - '{stepCount, number} {stepCount, plural, one {step} other {steps}} completed', - values: { - stepCount: completedSteps ?? 0, - }, - })} - + {completedText}

    + {completedText}
    ); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_run_details/components/step_number_nav.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_run_details/components/step_number_nav.tsx index 6a4b4ef710e79..8fb3a71f9c5f1 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_run_details/components/step_number_nav.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_run_details/components/step_number_nav.tsx @@ -36,6 +36,7 @@ export const StepNumberNav = ({ onClick={handlePreviousStep} disabled={!hasPreviousStep} iconType="arrowLeft" + aria-label={PREVIOUS_STEP_BUTTON_ARIA_LABEL} > {PREVIOUS_STEP_BUTTON_TEXT} @@ -59,6 +60,7 @@ export const StepNumberNav = ({ disabled={!hasNextStep} iconType="arrowRight" iconSide="right" + aria-label={NEXT_STEP_BUTTON_ARIA_LABEL} > {NEXT_STEP_BUTTON_TEXT} @@ -69,16 +71,30 @@ export const StepNumberNav = ({ ); }; -export const PREVIOUS_STEP_BUTTON_TEXT = i18n.translate( +const PREVIOUS_STEP_BUTTON_TEXT = i18n.translate( 'xpack.synthetics.synthetics.stepDetail.previousStepButtonText', { defaultMessage: 'Previous', } ); -export const NEXT_STEP_BUTTON_TEXT = i18n.translate( +const PREVIOUS_STEP_BUTTON_ARIA_LABEL = i18n.translate( + 'xpack.synthetics.synthetics.stepDetail.previousStepButtonAriaLabel', + { + defaultMessage: 'Previous step', + } +); + +const NEXT_STEP_BUTTON_TEXT = i18n.translate( 'xpack.synthetics.synthetics.stepDetail.nextStepButtonText', { defaultMessage: 'Next', } ); + +const NEXT_STEP_BUTTON_ARIA_LABEL = i18n.translate( + 'xpack.synthetics.synthetics.stepDetail.nextStepButtonAriaLabel', + { + defaultMessage: 'Next step', + } +); diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts index 6c381283c1824..650c9b964aaec 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.test.ts @@ -8,9 +8,35 @@ import { flattenAndFormatObject, getNormalizeCommonFields, + getUrlsField, + isValidURL, NormalizedProjectProps, } from './common_fields'; +describe('isValidUrl', () => { + it('returns false for invalid URL', () => { + expect(isValidURL('invalid')).toBeFalsy(); + }); + + it('returns true for valid URL', () => { + expect(isValidURL('https://elastic.co')).toBeTruthy(); + }); +}); + +describe('getUrlsField', () => { + it('supports a string value containing a comma', () => { + expect(getUrlsField('https://elastic.co?foo=bar,baz')).toEqual([ + 'https://elastic.co?foo=bar,baz', + ]); + }); + + it('supports lists containing exactly one entry, even with commas', () => { + expect(getUrlsField(['https://elastic.co?foo=bar,baz'])).toEqual([ + 'https://elastic.co?foo=bar,baz', + ]); + }); +}); + describe('normalizeYamlConfig', () => { it('does not continue flattening when encountering an array', () => { const array = ['value1', 'value2']; diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts index fad8e096a483c..4019151dc44f6 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/common_fields.ts @@ -306,6 +306,24 @@ export const getInvalidUrlsOrHostsError = ( ), }); +export const getUnparseableUrlError = (monitor: ProjectMonitor, version: string) => ({ + id: monitor.id, + reason: INVALID_CONFIGURATION_TITLE, + details: i18n.translate( + 'xpack.synthetics.projectMonitorApi.validation.unparseableUrl.description', + { + defaultMessage: + '`{monitorType}` project monitors must specify a valid URL for field `{key}` in version `{version}`. Your monitor definition with ID `{monitorId}` was not saved.', + values: { + monitorType: monitor.type, + key: 'monitor.urls', + version, + monitorId: monitor.id, + }, + } + ), +}); + const getInvalidLocationError = ( invalidPublic: string[], invalidPrivate: string[], @@ -356,6 +374,17 @@ export const getValueInSeconds = (value: string) => { return typeof valueInSeconds === 'number' ? `${valueInSeconds}` : null; }; +/** + * Accounts for url values in a string or list + * + * @param {Array | string} [value] + * @returns {array} Returns an array + */ +export const getUrlsField = (value?: string[] | string): string[] => { + if (!value) return []; + return Array.isArray(value) ? value : [value]; +}; + /** * Accounts for array values that are optionally defined as a comma seperated list * @@ -369,6 +398,20 @@ export const getOptionalListField = (value?: string[] | string): string[] => { return value ? value.split(',') : []; }; +/** + * Does a best-effort check to ensure that the `monitor.url` field will evaluate to a valid URL. + * @param url the value of a single entry in the `monitor.url` list intended to pass to the service + * @returns `true` if `new URL` does not throw an error, `false` otherwise + */ +export const isValidURL = (url: string): boolean => { + try { + new URL(url); + return true; + } catch { + return false; + } +}; + /** * Accounts for heartbeat fields that are optionally an array or single string * diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts index 0e92fd6155a5e..d77c03de45d48 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.test.ts @@ -48,7 +48,7 @@ describe('http normalizers', () => { enabled: false, id: 'my-monitor-2', name: 'My Monitor 2', - urls: ['http://localhost:9200', 'http://anotherurl:9200'], + urls: ['http://localhost:9200?withComma=1,2,3', 'http://anotherurl:9200'], schedule: 60, timeout: '80s', 'check.request': { @@ -202,7 +202,7 @@ describe('http normalizers', () => { tags: [], timeout: '80', type: 'http', - urls: 'http://localhost:9200', + urls: 'http://localhost:9200?withComma=1,2,3', 'url.port': null, username: '', id: '', @@ -374,7 +374,7 @@ describe('http normalizers', () => { tags: [], timeout: '80', type: 'http', - urls: 'http://localhost:9200', + urls: 'http://localhost:9200?withComma=1,2,3', 'url.port': null, username: '', id: '', diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts index 113b75924b1c1..caef8a1ba3381 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/normalizers/http_monitor.ts @@ -20,10 +20,12 @@ import { getNormalizeCommonFields, normalizeYamlConfig, getOptionalListField, - getOptionalArrayField, getUnsupportedKeysError, getInvalidUrlsOrHostsError, getHasTLSFields, + isValidURL, + getUnparseableUrlError, + getUrlsField, } from './common_fields'; export const getNormalizeHTTPFields = ({ @@ -50,9 +52,11 @@ export const getNormalizeHTTPFields = ({ errors.push(...commonErrors); /* Check if monitor has multiple urls */ - const urls = getOptionalListField(monitor.urls); + const urls = getUrlsField(monitor.urls); if (urls.length !== 1) { errors.push(getInvalidUrlsOrHostsError(monitor, 'urls', version)); + } else if (isValidURL(urls[0]) === false) { + errors.push(getUnparseableUrlError(monitor, version)); } if (unsupportedKeys.length) { errors.push(getUnsupportedKeysError(monitor, unsupportedKeys, version)); @@ -63,7 +67,7 @@ export const getNormalizeHTTPFields = ({ ...commonFields, [ConfigKey.MONITOR_TYPE]: MonitorTypeEnum.HTTP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.HTTP, - [ConfigKey.URLS]: getOptionalArrayField(monitor.urls) || defaultFields[ConfigKey.URLS], + [ConfigKey.URLS]: urls.length > 0 ? urls[0] : defaultFields[ConfigKey.URLS], [ConfigKey.MAX_REDIRECTS]: formatMaxRedirects(monitor[ConfigKey.MAX_REDIRECTS]), [ConfigKey.REQUEST_BODY_CHECK]: getRequestBodyField( (yamlConfig as Record)[ConfigKey.REQUEST_BODY_CHECK] as string, diff --git a/x-pack/plugins/observability_solution/uptime/kibana.jsonc b/x-pack/plugins/observability_solution/uptime/kibana.jsonc index 3e27bac31cba6..b45d8b78bc9cc 100644 --- a/x-pack/plugins/observability_solution/uptime/kibana.jsonc +++ b/x-pack/plugins/observability_solution/uptime/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/uptime-plugin", - "owner": "@elastic/obs-ux-infra_services-team", + "owner": "@elastic/obs-ux-management-team", "description": "This plugin visualizes data from Heartbeat, and integrates with other Observability solutions.", "plugin": { "id": "uptime", diff --git a/x-pack/plugins/osquery/common/api/live_query/create_live_query.gen.ts b/x-pack/plugins/osquery/common/api/live_query/create_live_query.gen.ts index a0635327aaf30..e435a79acdab4 100644 --- a/x-pack/plugins/osquery/common/api/live_query/create_live_query.gen.ts +++ b/x-pack/plugins/osquery/common/api/live_query/create_live_query.gen.ts @@ -40,6 +40,3 @@ export const CreateLiveQueryRequestBody = z.object({ event_ids: z.array(z.string()).optional(), metadata: z.object({}).nullable().optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/live_query/create_live_query.schema.yaml b/x-pack/plugins/osquery/common/api/live_query/create_live_query.schema.yaml index 4cae65cab8b82..1e40158c638d7 100644 --- a/x-pack/plugins/osquery/common/api/live_query/create_live_query.schema.yaml +++ b/x-pack/plugins/osquery/common/api/live_query/create_live_query.schema.yaml @@ -47,7 +47,3 @@ components: metadata: type: object nullable: true - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/live_query/find_live_query.gen.ts b/x-pack/plugins/osquery/common/api/live_query/find_live_query.gen.ts index 4860fae7cb7ca..313bb98064a50 100644 --- a/x-pack/plugins/osquery/common/api/live_query/find_live_query.gen.ts +++ b/x-pack/plugins/osquery/common/api/live_query/find_live_query.gen.ts @@ -32,6 +32,3 @@ export const FindLiveQueryRequestQuery = z.object({ sort: SortOrUndefined.optional(), sortOrder: SortOrderOrUndefined.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/live_query/find_live_query.schema.yaml b/x-pack/plugins/osquery/common/api/live_query/find_live_query.schema.yaml index 730051c5a329d..50589ecc9fc28 100644 --- a/x-pack/plugins/osquery/common/api/live_query/find_live_query.schema.yaml +++ b/x-pack/plugins/osquery/common/api/live_query/find_live_query.schema.yaml @@ -4,13 +4,6 @@ info: version: '2023-10-31' paths: { } components: - parameters: - FindLiveQueryRequestQueryParameter: - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/FindLiveQueryRequestQuery' schemas: FindLiveQueryRequestQuery: type: object @@ -25,7 +18,3 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SortOrUndefined' sortOrder: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SortOrderOrUndefined' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/live_query/get_live_query_details.gen.ts b/x-pack/plugins/osquery/common/api/live_query/get_live_query_details.gen.ts deleted file mode 100644 index 503b7f79f5a71..0000000000000 --- a/x-pack/plugins/osquery/common/api/live_query/get_live_query_details.gen.ts +++ /dev/null @@ -1,20 +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. - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Get Live Query Details Schema - * version: 2023-10-31 - */ - -import { z } from 'zod'; - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/live_query/get_live_query_details.schema.yaml b/x-pack/plugins/osquery/common/api/live_query/get_live_query_details.schema.yaml deleted file mode 100644 index 8bc32f55162f4..0000000000000 --- a/x-pack/plugins/osquery/common/api/live_query/get_live_query_details.schema.yaml +++ /dev/null @@ -1,24 +0,0 @@ -openapi: 3.0.0 -info: - title: Get Live Query Details Schema - version: '2023-10-31' -paths: { } -components: - parameters: - GetLiveQueryDetailsRequestParameter: - name: id - in: path - required: true - schema: - $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Id' - GetLiveQueryDetailsRequestQueryParameter: - name: query - in: query - schema: - type: object - additionalProperties: true - schemas: - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.gen.ts b/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.gen.ts index 171755c49941e..b1ef683502bc3 100644 --- a/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.gen.ts +++ b/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.gen.ts @@ -39,6 +39,3 @@ export const GetLiveQueryResultsRequestParams = z.object({ id: Id.optional(), actionId: Id.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.schema.yaml b/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.schema.yaml index 6036820ef022b..9d946d6fe821d 100644 --- a/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.schema.yaml +++ b/x-pack/plugins/osquery/common/api/live_query/get_live_query_results.schema.yaml @@ -4,19 +4,6 @@ info: version: '2023-10-31' paths: { } components: - parameters: - GetLiveQueryRequestResultsQueryParameter: - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/GetLiveQueryResultsRequestQuery' - GetLiveQueryRequestResultsParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/GetLiveQueryResultsRequestParams' schemas: GetLiveQueryResultsRequestQuery: type: object @@ -38,8 +25,3 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Id' actionId: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Id' - - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/live_query/live_queries.gen.ts b/x-pack/plugins/osquery/common/api/live_query/live_queries.gen.ts new file mode 100644 index 0000000000000..03c760cf90f76 --- /dev/null +++ b/x-pack/plugins/osquery/common/api/live_query/live_queries.gen.ts @@ -0,0 +1,88 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Live Queries Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { FindLiveQueryRequestQuery } from './find_live_query.gen'; +import { DefaultSuccessResponse, Id } from '../model/schema/common_attributes.gen'; +import { CreateLiveQueryRequestBody } from './create_live_query.gen'; +import { + GetLiveQueryResultsRequestQuery, + GetLiveQueryResultsRequestParams, +} from './get_live_query_results.gen'; + +export type OsqueryCreateLiveQueryRequestBody = z.infer; +export const OsqueryCreateLiveQueryRequestBody = CreateLiveQueryRequestBody; +export type OsqueryCreateLiveQueryRequestBodyInput = z.input< + typeof OsqueryCreateLiveQueryRequestBody +>; + +export type OsqueryCreateLiveQueryResponse = z.infer; +export const OsqueryCreateLiveQueryResponse = DefaultSuccessResponse; +export type OsqueryFindLiveQueriesRequestQuery = z.infer; +export const OsqueryFindLiveQueriesRequestQuery = z.object({ + query: FindLiveQueryRequestQuery, +}); +export type OsqueryFindLiveQueriesRequestQueryInput = z.input< + typeof OsqueryFindLiveQueriesRequestQuery +>; + +export type OsqueryFindLiveQueriesResponse = z.infer; +export const OsqueryFindLiveQueriesResponse = DefaultSuccessResponse; +export type OsqueryGetLiveQueryDetailsRequestQuery = z.infer< + typeof OsqueryGetLiveQueryDetailsRequestQuery +>; +export const OsqueryGetLiveQueryDetailsRequestQuery = z.object({ + query: z.object({}), +}); +export type OsqueryGetLiveQueryDetailsRequestQueryInput = z.input< + typeof OsqueryGetLiveQueryDetailsRequestQuery +>; + +export type OsqueryGetLiveQueryDetailsRequestParams = z.infer< + typeof OsqueryGetLiveQueryDetailsRequestParams +>; +export const OsqueryGetLiveQueryDetailsRequestParams = z.object({ + id: Id, +}); +export type OsqueryGetLiveQueryDetailsRequestParamsInput = z.input< + typeof OsqueryGetLiveQueryDetailsRequestParams +>; + +export type OsqueryGetLiveQueryDetailsResponse = z.infer; +export const OsqueryGetLiveQueryDetailsResponse = DefaultSuccessResponse; +export type OsqueryGetLiveQueryResultsRequestQuery = z.infer< + typeof OsqueryGetLiveQueryResultsRequestQuery +>; +export const OsqueryGetLiveQueryResultsRequestQuery = z.object({ + query: GetLiveQueryResultsRequestQuery, +}); +export type OsqueryGetLiveQueryResultsRequestQueryInput = z.input< + typeof OsqueryGetLiveQueryResultsRequestQuery +>; + +export type OsqueryGetLiveQueryResultsRequestParams = z.infer< + typeof OsqueryGetLiveQueryResultsRequestParams +>; +export const OsqueryGetLiveQueryResultsRequestParams = z.object({ + query: GetLiveQueryResultsRequestParams, +}); +export type OsqueryGetLiveQueryResultsRequestParamsInput = z.input< + typeof OsqueryGetLiveQueryResultsRequestParams +>; + +export type OsqueryGetLiveQueryResultsResponse = z.infer; +export const OsqueryGetLiveQueryResultsResponse = DefaultSuccessResponse; diff --git a/x-pack/plugins/osquery/common/api/live_query/live_queries.schema.yaml b/x-pack/plugins/osquery/common/api/live_query/live_queries.schema.yaml index feff5dd665f33..ae7c9e4d50f2d 100644 --- a/x-pack/plugins/osquery/common/api/live_query/live_queries.schema.yaml +++ b/x-pack/plugins/osquery/common/api/live_query/live_queries.schema.yaml @@ -6,17 +6,32 @@ paths: /api/osquery/live_queries: get: summary: Find live queries + operationId: OsqueryFindLiveQueries + x-codegen-enabled: true + x-labels: + - ess + - serverless parameters: - - $ref: './find_live_query.schema.yaml#/components/parameters/FindLiveQueryRequestQueryParameter' + - name: query + in: query + required: true + schema: + $ref: './find_live_query.schema.yaml#/components/schemas/FindLiveQueryRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './find_live_query.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' + post: summary: Create a live query + operationId: OsqueryCreateLiveQuery + x-codegen-enabled: true + x-labels: + - ess + - serverless requestBody: required: true content: @@ -29,30 +44,59 @@ paths: content: application/json: schema: - $ref: './create_live_query.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' + /api/osquery/live_queries/{id}: get: summary: Get live query details + operationId: OsqueryGetLiveQueryDetails + x-codegen-enabled: true + x-labels: + - ess + - serverless parameters: - - $ref: './get_live_query_details.schema.yaml#/components/parameters/GetLiveQueryDetailsRequestQueryParameter' - - $ref: './get_live_query_details.schema.yaml#/components/parameters/GetLiveQueryDetailsRequestParameter' + - name: id + in: path + required: true + schema: + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Id' + - name: query + in: query + schema: + type: object + additionalProperties: true responses: '200': description: OK content: application/json: schema: - $ref: './get_live_query_details.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' + /api/osquery/live_queries/{id}/results/{actionId}: - get: - summary: Get live query results - parameters: - - $ref: './get_live_query_results.schema.yaml#/components/parameters/GetLiveQueryRequestResultsQueryParameter' - - $ref: './get_live_query_results.schema.yaml#/components/parameters/GetLiveQueryRequestResultsParameter' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: './get_live_query_results.schema.yaml#/components/schemas/SuccessResponse' + get: + summary: Get live query results + operationId: OsqueryGetLiveQueryResults + x-codegen-enabled: true + x-labels: + - ess + - serverless + parameters: + - name: query + in: query + required: true + schema: + $ref: './get_live_query_results.schema.yaml#/components/schemas/GetLiveQueryResultsRequestQuery' + - name: query + in: path + required: true + schema: + $ref: './get_live_query_results.schema.yaml#/components/schemas/GetLiveQueryResultsRequestParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' + diff --git a/x-pack/plugins/osquery/common/api/model/schema/common_attributes.gen.ts b/x-pack/plugins/osquery/common/api/model/schema/common_attributes.gen.ts index f57cd361a0dd2..71cfeda094ff9 100644 --- a/x-pack/plugins/osquery/common/api/model/schema/common_attributes.gen.ts +++ b/x-pack/plugins/osquery/common/api/model/schema/common_attributes.gen.ts @@ -178,3 +178,6 @@ export const SortOrderOrUndefined = z.union([z.string().nullable(), z.unknown()] export type Shards = z.infer; export const Shards = z.object({}).catchall(z.number()); + +export type DefaultSuccessResponse = z.infer; +export const DefaultSuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/model/schema/common_attributes.schema.yaml b/x-pack/plugins/osquery/common/api/model/schema/common_attributes.schema.yaml index ba236b8601e7d..9f24757ee300c 100644 --- a/x-pack/plugins/osquery/common/api/model/schema/common_attributes.schema.yaml +++ b/x-pack/plugins/osquery/common/api/model/schema/common_attributes.schema.yaml @@ -241,11 +241,15 @@ components: SortOrderOrUndefined: oneOf: - - type: string - nullable: true - - enum: [ asc, desc ] + - type: string + nullable: true + - enum: [ asc, desc ] Shards: type: object additionalProperties: type: number + + DefaultSuccessResponse: + type: object + properties: { } diff --git a/x-pack/plugins/osquery/common/api/packs/create_pack.gen.ts b/x-pack/plugins/osquery/common/api/packs/create_pack.gen.ts index 54cd665b25750..1269df0f65053 100644 --- a/x-pack/plugins/osquery/common/api/packs/create_pack.gen.ts +++ b/x-pack/plugins/osquery/common/api/packs/create_pack.gen.ts @@ -34,6 +34,3 @@ export const CreatePacksRequestBody = z.object({ shards: Shards.optional(), queries: ObjectQueries.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/packs/create_pack.schema.yaml b/x-pack/plugins/osquery/common/api/packs/create_pack.schema.yaml index da04d037b1d56..cac8b877bac1a 100644 --- a/x-pack/plugins/osquery/common/api/packs/create_pack.schema.yaml +++ b/x-pack/plugins/osquery/common/api/packs/create_pack.schema.yaml @@ -20,7 +20,4 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Shards' queries: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/ObjectQueries' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed + diff --git a/x-pack/plugins/osquery/common/api/packs/delete_packs.gen.ts b/x-pack/plugins/osquery/common/api/packs/delete_packs.gen.ts index 4efab4f488a1d..525950384cc2f 100644 --- a/x-pack/plugins/osquery/common/api/packs/delete_packs.gen.ts +++ b/x-pack/plugins/osquery/common/api/packs/delete_packs.gen.ts @@ -22,6 +22,3 @@ export type DeletePacksRequestQuery = z.infer; export const DeletePacksRequestQuery = z.object({ id: PackId.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/packs/delete_packs.schema.yaml b/x-pack/plugins/osquery/common/api/packs/delete_packs.schema.yaml index 3286aa0b1bb7a..b26c5e415450a 100644 --- a/x-pack/plugins/osquery/common/api/packs/delete_packs.schema.yaml +++ b/x-pack/plugins/osquery/common/api/packs/delete_packs.schema.yaml @@ -4,20 +4,9 @@ info: version: '2023-10-31' paths: { } components: - parameters: - DeletePacksRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/DeletePacksRequestQuery' schemas: DeletePacksRequestQuery: type: object properties: id: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/PackId' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/packs/find_packs.gen.ts b/x-pack/plugins/osquery/common/api/packs/find_packs.gen.ts index 9ff3e8c6ae0df..dcad0cb0c9757 100644 --- a/x-pack/plugins/osquery/common/api/packs/find_packs.gen.ts +++ b/x-pack/plugins/osquery/common/api/packs/find_packs.gen.ts @@ -30,6 +30,3 @@ export const FindPacksRequestQuery = z.object({ sort: SortOrUndefined.optional(), sortOrder: SortOrderOrUndefined.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/packs/find_packs.schema.yaml b/x-pack/plugins/osquery/common/api/packs/find_packs.schema.yaml index 4cd1c222bc6d2..c93d9249b7299 100644 --- a/x-pack/plugins/osquery/common/api/packs/find_packs.schema.yaml +++ b/x-pack/plugins/osquery/common/api/packs/find_packs.schema.yaml @@ -4,13 +4,6 @@ info: version: '2023-10-31' paths: { } components: - parameters: - FindPacksRequestQueryParameter: - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/FindPacksRequestQuery' schemas: FindPacksRequestQuery: type: object @@ -23,7 +16,3 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SortOrUndefined' sortOrder: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SortOrderOrUndefined' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/packs/packs.gen.ts b/x-pack/plugins/osquery/common/api/packs/packs.gen.ts new file mode 100644 index 0000000000000..26d5e8536b884 --- /dev/null +++ b/x-pack/plugins/osquery/common/api/packs/packs.gen.ts @@ -0,0 +1,74 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Packs Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { FindPacksRequestQuery } from './find_packs.gen'; +import { DefaultSuccessResponse } from '../model/schema/common_attributes.gen'; +import { CreatePacksRequestBody } from './create_pack.gen'; +import { ReadPacksRequestQuery } from './read_packs.gen'; +import { DeletePacksRequestQuery } from './delete_packs.gen'; +import { UpdatePacksRequestBody, UpdatePacksRequestParams } from './update_packs.gen'; + +export type OsqueryCreatePacksRequestBody = z.infer; +export const OsqueryCreatePacksRequestBody = CreatePacksRequestBody; +export type OsqueryCreatePacksRequestBodyInput = z.input; + +export type OsqueryCreatePacksResponse = z.infer; +export const OsqueryCreatePacksResponse = DefaultSuccessResponse; + +export type OsqueryDeletePacksRequestParams = z.infer; +export const OsqueryDeletePacksRequestParams = z.object({ + query: DeletePacksRequestQuery, +}); +export type OsqueryDeletePacksRequestParamsInput = z.input; + +export type OsqueryDeletePacksResponse = z.infer; +export const OsqueryDeletePacksResponse = DefaultSuccessResponse; +export type OsqueryFindPacksRequestQuery = z.infer; +export const OsqueryFindPacksRequestQuery = z.object({ + query: FindPacksRequestQuery, +}); +export type OsqueryFindPacksRequestQueryInput = z.input; + +export type OsqueryFindPacksResponse = z.infer; +export const OsqueryFindPacksResponse = DefaultSuccessResponse; + +export type OsqueryGetPacksDetailsRequestParams = z.infer< + typeof OsqueryGetPacksDetailsRequestParams +>; +export const OsqueryGetPacksDetailsRequestParams = z.object({ + query: ReadPacksRequestQuery, +}); +export type OsqueryGetPacksDetailsRequestParamsInput = z.input< + typeof OsqueryGetPacksDetailsRequestParams +>; + +export type OsqueryGetPacksDetailsResponse = z.infer; +export const OsqueryGetPacksDetailsResponse = DefaultSuccessResponse; + +export type OsqueryUpdatePacksRequestParams = z.infer; +export const OsqueryUpdatePacksRequestParams = z.object({ + query: UpdatePacksRequestParams, +}); +export type OsqueryUpdatePacksRequestParamsInput = z.input; + +export type OsqueryUpdatePacksRequestBody = z.infer; +export const OsqueryUpdatePacksRequestBody = UpdatePacksRequestBody; +export type OsqueryUpdatePacksRequestBodyInput = z.input; + +export type OsqueryUpdatePacksResponse = z.infer; +export const OsqueryUpdatePacksResponse = DefaultSuccessResponse; diff --git a/x-pack/plugins/osquery/common/api/packs/packs.schema.yaml b/x-pack/plugins/osquery/common/api/packs/packs.schema.yaml index 006e0ebd75286..bb2078e334649 100644 --- a/x-pack/plugins/osquery/common/api/packs/packs.schema.yaml +++ b/x-pack/plugins/osquery/common/api/packs/packs.schema.yaml @@ -6,17 +6,31 @@ paths: /api/osquery/packs: get: summary: Find packs + operationId: OsqueryFindPacks + x-codegen-enabled: true + x-labels: + - ess + - serverless parameters: - - $ref: './find_packs.schema.yaml#/components/parameters/FindPacksRequestQueryParameter' + - name: query + in: query + required: true + schema: + $ref: './find_packs.schema.yaml#/components/schemas/FindPacksRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './find_packs.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' post: summary: Create a packs + operationId: OsqueryCreatePacks + x-codegen-enabled: true + x-labels: + - ess + - serverless requestBody: required: true content: @@ -29,32 +43,55 @@ paths: content: application/json: schema: - $ref: './create_pack.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' /api/osquery/packs/{id}: get: summary: Get packs details + operationId: OsqueryGetPacksDetails + x-codegen-enabled: true + x-labels: + - ess + - serverless parameters: - - $ref: './read_packs.schema.yaml#/components/parameters/ReadPacksRequestQueryParameter' + - name: query + in: path + required: true + schema: + $ref: './read_packs.schema.yaml#/components/schemas/ReadPacksRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './read_packs.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' delete: summary: Delete packs + operationId: OsqueryDeletePacks + x-codegen-enabled: true + x-labels: + - ess + - serverless parameters: - - $ref: './delete_packs.schema.yaml#/components/parameters/DeletePacksRequestQueryParameter' + - name: query + in: path + required: true + schema: + $ref: './delete_packs.schema.yaml#/components/schemas/DeletePacksRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './find_packs.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' put: summary: Update packs + operationId: OsqueryUpdatePacks + x-codegen-enabled: true + x-labels: + - ess + - serverless requestBody: required: true content: @@ -62,11 +99,15 @@ paths: schema: $ref: './update_packs.schema.yaml#/components/schemas/UpdatePacksRequestBody' parameters: - - $ref: './update_packs.schema.yaml#/components/parameters/UpdatePacksRequestQueryParameter' + - name: query + in: path + required: true + schema: + $ref: './update_packs.schema.yaml#/components/schemas/UpdatePacksRequestParams' responses: '200': description: OK content: application/json: schema: - $ref: './update_packs.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' diff --git a/x-pack/plugins/osquery/common/api/packs/read_packs.gen.ts b/x-pack/plugins/osquery/common/api/packs/read_packs.gen.ts index bd125eb3acf05..2a315b1604db0 100644 --- a/x-pack/plugins/osquery/common/api/packs/read_packs.gen.ts +++ b/x-pack/plugins/osquery/common/api/packs/read_packs.gen.ts @@ -22,6 +22,3 @@ export type ReadPacksRequestQuery = z.infer; export const ReadPacksRequestQuery = z.object({ id: PackId.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/packs/read_packs.schema.yaml b/x-pack/plugins/osquery/common/api/packs/read_packs.schema.yaml index 8cfe415848c92..de068fca1259e 100644 --- a/x-pack/plugins/osquery/common/api/packs/read_packs.schema.yaml +++ b/x-pack/plugins/osquery/common/api/packs/read_packs.schema.yaml @@ -4,20 +4,9 @@ info: version: '2023-10-31' paths: { } components: - parameters: - ReadPacksRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/ReadPacksRequestQuery' schemas: ReadPacksRequestQuery: type: object properties: id: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/PackId' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/packs/update_packs.gen.ts b/x-pack/plugins/osquery/common/api/packs/update_packs.gen.ts index 92321233cd0d0..de75ec2e3ebc2 100644 --- a/x-pack/plugins/osquery/common/api/packs/update_packs.gen.ts +++ b/x-pack/plugins/osquery/common/api/packs/update_packs.gen.ts @@ -39,6 +39,3 @@ export const UpdatePacksRequestBody = z.object({ shards: Shards.optional(), queries: ObjectQueries.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/packs/update_packs.schema.yaml b/x-pack/plugins/osquery/common/api/packs/update_packs.schema.yaml index 0b0510b4773ab..5a10ec7a73da4 100644 --- a/x-pack/plugins/osquery/common/api/packs/update_packs.schema.yaml +++ b/x-pack/plugins/osquery/common/api/packs/update_packs.schema.yaml @@ -4,13 +4,6 @@ info: version: '2023-10-31' paths: { } components: - parameters: - UpdatePacksRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/UpdatePacksRequestParams' schemas: UpdatePacksRequestParams: type: object @@ -32,7 +25,3 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Shards' queries: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/ObjectQueries' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.gen.ts b/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.gen.ts index da550104fb6f1..daaee3e18c65c 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.gen.ts +++ b/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.gen.ts @@ -22,6 +22,3 @@ export type DeleteSavedQueryRequestQuery = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.schema.yaml b/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.schema.yaml index 7a180c542aa23..7c301cee02242 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.schema.yaml +++ b/x-pack/plugins/osquery/common/api/saved_query/delete_saved_query.schema.yaml @@ -4,20 +4,9 @@ info: version: '2023-10-31' paths: { } components: - parameters: - DeleteSavedQueryRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/DeleteSavedQueryRequestQuery' schemas: DeleteSavedQueryRequestQuery: type: object properties: id: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SavedQueryId' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.gen.ts b/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.gen.ts index 0c875aa8fa187..2773d1f5e210d 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.gen.ts +++ b/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.gen.ts @@ -30,6 +30,3 @@ export const FindSavedQueryRequestQuery = z.object({ sort: SortOrUndefined.optional(), sortOrder: SortOrderOrUndefined.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.schema.yaml b/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.schema.yaml index dbebf003a4696..ba11befc7af28 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.schema.yaml +++ b/x-pack/plugins/osquery/common/api/saved_query/find_saved_query.schema.yaml @@ -4,13 +4,6 @@ info: version: '2023-10-31' paths: { } components: - parameters: - FindSavedQueryRequestQueryParameter: - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/FindSavedQueryRequestQuery' schemas: FindSavedQueryRequestQuery: type: object @@ -23,7 +16,3 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SortOrUndefined' sortOrder: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SortOrderOrUndefined' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.gen.ts b/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.gen.ts index 33708f3fc4f3c..d397135a87286 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.gen.ts +++ b/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.gen.ts @@ -22,6 +22,3 @@ export type ReadSavedQueryRequestQuery = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.schema.yaml b/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.schema.yaml index a5fed00a37e0c..7f2db7145e673 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.schema.yaml +++ b/x-pack/plugins/osquery/common/api/saved_query/read_saved_query.schema.yaml @@ -4,20 +4,9 @@ info: version: '2023-10-31' paths: { } components: - parameters: - ReadSavedQueryRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/ReadSavedQueryRequestQuery' schemas: ReadSavedQueryRequestQuery: type: object properties: id: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SavedQueryId' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/saved_query/saved_query.gen.ts b/x-pack/plugins/osquery/common/api/saved_query/saved_query.gen.ts new file mode 100644 index 0000000000000..033dab65762dd --- /dev/null +++ b/x-pack/plugins/osquery/common/api/saved_query/saved_query.gen.ts @@ -0,0 +1,95 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Saved Queries Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { FindSavedQueryRequestQuery } from './find_saved_query.gen'; +import { DefaultSuccessResponse } from '../model/schema/common_attributes.gen'; +import { CreateSavedQueryRequestBody } from './create_saved_query.gen'; +import { ReadSavedQueryRequestQuery } from './read_saved_query.gen'; +import { DeleteSavedQueryRequestQuery } from './delete_saved_query.gen'; +import { + UpdateSavedQueryRequestBody, + UpdateSavedQueryRequestParams, +} from './update_saved_query.gen'; + +export type OsqueryCreateSavedQueryRequestBody = z.infer; +export const OsqueryCreateSavedQueryRequestBody = CreateSavedQueryRequestBody; +export type OsqueryCreateSavedQueryRequestBodyInput = z.input< + typeof OsqueryCreateSavedQueryRequestBody +>; + +export type OsqueryCreateSavedQueryResponse = z.infer; +export const OsqueryCreateSavedQueryResponse = DefaultSuccessResponse; + +export type OsqueryDeleteSavedQueryRequestParams = z.infer< + typeof OsqueryDeleteSavedQueryRequestParams +>; +export const OsqueryDeleteSavedQueryRequestParams = z.object({ + query: DeleteSavedQueryRequestQuery, +}); +export type OsqueryDeleteSavedQueryRequestParamsInput = z.input< + typeof OsqueryDeleteSavedQueryRequestParams +>; + +export type OsqueryDeleteSavedQueryResponse = z.infer; +export const OsqueryDeleteSavedQueryResponse = DefaultSuccessResponse; +export type OsqueryFindSavedQueriesRequestQuery = z.infer< + typeof OsqueryFindSavedQueriesRequestQuery +>; +export const OsqueryFindSavedQueriesRequestQuery = z.object({ + query: FindSavedQueryRequestQuery, +}); +export type OsqueryFindSavedQueriesRequestQueryInput = z.input< + typeof OsqueryFindSavedQueriesRequestQuery +>; + +export type OsqueryFindSavedQueriesResponse = z.infer; +export const OsqueryFindSavedQueriesResponse = DefaultSuccessResponse; + +export type OsqueryGetSavedQueryDetailsRequestParams = z.infer< + typeof OsqueryGetSavedQueryDetailsRequestParams +>; +export const OsqueryGetSavedQueryDetailsRequestParams = z.object({ + query: ReadSavedQueryRequestQuery, +}); +export type OsqueryGetSavedQueryDetailsRequestParamsInput = z.input< + typeof OsqueryGetSavedQueryDetailsRequestParams +>; + +export type OsqueryGetSavedQueryDetailsResponse = z.infer< + typeof OsqueryGetSavedQueryDetailsResponse +>; +export const OsqueryGetSavedQueryDetailsResponse = DefaultSuccessResponse; + +export type OsqueryUpdateSavedQueryRequestParams = z.infer< + typeof OsqueryUpdateSavedQueryRequestParams +>; +export const OsqueryUpdateSavedQueryRequestParams = z.object({ + query: UpdateSavedQueryRequestParams, +}); +export type OsqueryUpdateSavedQueryRequestParamsInput = z.input< + typeof OsqueryUpdateSavedQueryRequestParams +>; + +export type OsqueryUpdateSavedQueryRequestBody = z.infer; +export const OsqueryUpdateSavedQueryRequestBody = UpdateSavedQueryRequestBody; +export type OsqueryUpdateSavedQueryRequestBodyInput = z.input< + typeof OsqueryUpdateSavedQueryRequestBody +>; + +export type OsqueryUpdateSavedQueryResponse = z.infer; +export const OsqueryUpdateSavedQueryResponse = DefaultSuccessResponse; diff --git a/x-pack/plugins/osquery/common/api/saved_query/saved_query.schema.yaml b/x-pack/plugins/osquery/common/api/saved_query/saved_query.schema.yaml index d8cef82ac7103..62ae77a83a94b 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/saved_query.schema.yaml +++ b/x-pack/plugins/osquery/common/api/saved_query/saved_query.schema.yaml @@ -6,17 +6,31 @@ paths: /api/osquery/saved_queries: get: summary: Find saved queries + operationId: OsqueryFindSavedQueries + x-codegen-enabled: true + x-labels: + - ess + - serverless parameters: - - $ref: './find_saved_query.schema.yaml#/components/parameters/FindSavedQueryRequestQueryParameter' + - name: query + in: query + required: true + schema: + $ref: './find_saved_query.schema.yaml#/components/schemas/FindSavedQueryRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './find_saved_query.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' post: summary: Create a saved query + operationId: OsqueryCreateSavedQuery + x-codegen-enabled: true + x-labels: + - ess + - serverless requestBody: required: true content: @@ -29,44 +43,71 @@ paths: content: application/json: schema: - $ref: './create_saved_query.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' /api/osquery/saved_queries/{id}: get: summary: Get saved query details + operationId: OsqueryGetSavedQueryDetails + x-codegen-enabled: true + x-labels: + - ess + - serverless parameters: - - $ref: './read_saved_query.schema.yaml#/components/parameters/ReadSavedQueryRequestQueryParameter' + - name: query + in: path + required: true + schema: + $ref: './read_saved_query.schema.yaml#/components/schemas/ReadSavedQueryRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './read_saved_query.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' delete: - summary: Delete saved query - parameters: - - $ref: './delete_saved_query.schema.yaml#/components/parameters/DeleteSavedQueryRequestQueryParameter' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: './find_saved_query.schema.yaml#/components/schemas/SuccessResponse' + summary: Delete saved query + operationId: OsqueryDeleteSavedQuery + x-codegen-enabled: true + x-labels: + - ess + - serverless + parameters: + - name: query + in: path + required: true + schema: + $ref: './delete_saved_query.schema.yaml#/components/schemas/DeleteSavedQueryRequestQuery' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' put: - summary: Update saved query - requestBody: - required: true - content: - application/json: - schema: - $ref: './update_saved_query.schema.yaml#/components/schemas/UpdateSavedQueryRequestBody' - parameters: - - $ref: './update_saved_query.schema.yaml#/components/parameters/UpdateSavedQueryRequestQueryParameter' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: './update_saved_query.schema.yaml#/components/schemas/SuccessResponse' + summary: Update saved query + operationId: OsqueryUpdateSavedQuery + x-codegen-enabled: true + x-labels: + - ess + - serverless + requestBody: + required: true + content: + application/json: + schema: + $ref: './update_saved_query.schema.yaml#/components/schemas/UpdateSavedQueryRequestBody' + parameters: + - name: query + in: path + required: true + schema: + $ref: './update_saved_query.schema.yaml#/components/schemas/UpdateSavedQueryRequestParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/DefaultSuccessResponse' diff --git a/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.gen.ts b/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.gen.ts index 70417f2880de2..b2e5a48c4aaca 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.gen.ts +++ b/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.gen.ts @@ -44,6 +44,3 @@ export const UpdateSavedQueryRequestBody = z.object({ snapshot: SnapshotOrUndefined.optional(), removed: RemovedOrUndefined.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.schema.yaml b/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.schema.yaml index b91359b5bbeef..9f33e8bfda83d 100644 --- a/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.schema.yaml +++ b/x-pack/plugins/osquery/common/api/saved_query/update_saved_query.schema.yaml @@ -4,13 +4,6 @@ info: version: '2023-10-31' paths: { } components: - parameters: - UpdateSavedQueryRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/UpdateSavedQueryRequestParams' schemas: UpdateSavedQueryRequestParams: type: object @@ -38,7 +31,3 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/SnapshotOrUndefined' removed: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/RemovedOrUndefined' - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts index 6b847fb396967..f06666ed1fd88 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts @@ -39,7 +39,8 @@ import { } from '../../tasks/integrations'; import { ServerlessRoleName } from '../../support/roles'; -describe('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/170593 +describe.skip('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () => { let savedQueryId: string; before(() => { @@ -103,7 +104,8 @@ describe('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () => { cy.contains(`version: ${oldVersion}`).should('not.exist'); }); }); - describe('Add integration to policy', () => { + // FLAKY: https://github.com/elastic/kibana/issues/170593 + describe.skip('Add integration to policy', () => { const [integrationName, policyName] = generateRandomStringName(2); let policyId: string; beforeEach(() => { diff --git a/x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts index ccbd119aab3a7..f940807345034 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/packs_integration.cy.ts @@ -40,7 +40,9 @@ import { cleanupPack, cleanupAgentPolicy } from '../../tasks/api_fixtures'; import { request } from '../../tasks/common'; import { ServerlessRoleName } from '../../support/roles'; -describe('ALL - Packs', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/171279 +// Failing: See https://github.com/elastic/kibana/issues/180424 +describe.skip('ALL - Packs', { tags: ['@ess', '@serverless'] }, () => { const integration = 'Osquery Manager'; describe( diff --git a/x-pack/plugins/search_homepage/public/components/console_link_button.tsx b/x-pack/plugins/search_homepage/public/components/console_link_button.tsx new file mode 100644 index 0000000000000..79511788b2f95 --- /dev/null +++ b/x-pack/plugins/search_homepage/public/components/console_link_button.tsx @@ -0,0 +1,45 @@ +/* + * 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 React, { useCallback } from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { ConsolePluginStart } from '@kbn/console-plugin/public'; + +import { useKibana } from '../hooks/use_kibana'; + +const canOpenConsole = (plugin?: ConsolePluginStart): boolean => { + if (!plugin) return false; + if (!plugin.isEmbeddedConsoleAvailable || !plugin.openEmbeddedConsole) return false; + return plugin.isEmbeddedConsoleAvailable(); +}; + +export const ConsoleLinkButton = () => { + const { + services: { console: consolePlugin }, + } = useKibana(); + const openConsole = useCallback(() => { + if (!canOpenConsole(consolePlugin)) return; + + consolePlugin!.openEmbeddedConsole!(); + }, [consolePlugin]); + if (consolePlugin === undefined || consolePlugin.openEmbeddedConsole === undefined) return null; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/search_homepage/public/components/endpoints_header_action.tsx b/x-pack/plugins/search_homepage/public/components/endpoints_header_action.tsx new file mode 100644 index 0000000000000..0cc3ae6dea48f --- /dev/null +++ b/x-pack/plugins/search_homepage/public/components/endpoints_header_action.tsx @@ -0,0 +1,45 @@ +/* + * 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 React from 'react'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlyout, EuiHeaderLinks } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + KibanaWiredConnectionDetailsProvider, + ConnectionDetailsFlyoutContent, +} from '@kbn/cloud/connection_details'; + +export const EndpointsHeaderAction = () => { + const [open, setOpen] = React.useState(false); + + return ( + <> + + + setOpen(true)} + data-test-subj="searchHomepageEndpointsHeaderActionEndpointsApiKeysButton" + data-telemetry-id="searchHomepageEndpointsHeaderActionEndpointsApiKeysButton" + > + + + + + {open && ( + setOpen(false)} size="s"> + + + + + )} + + ); +}; diff --git a/x-pack/plugins/search_homepage/public/components/search_homepage.tsx b/x-pack/plugins/search_homepage/public/components/search_homepage.tsx index 7af02cbcf3275..76f34caad0a5d 100644 --- a/x-pack/plugins/search_homepage/public/components/search_homepage.tsx +++ b/x-pack/plugins/search_homepage/public/components/search_homepage.tsx @@ -24,7 +24,7 @@ export const SearchHomepagePage = () => { return ( - + {embeddableConsole} diff --git a/x-pack/plugins/search_homepage/public/components/search_homepage_body.tsx b/x-pack/plugins/search_homepage/public/components/search_homepage_body.tsx index 808393594e7d8..f27da23468711 100644 --- a/x-pack/plugins/search_homepage/public/components/search_homepage_body.tsx +++ b/x-pack/plugins/search_homepage/public/components/search_homepage_body.tsx @@ -6,19 +6,16 @@ */ import React from 'react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import { ConsoleLinkButton } from './console_link_button'; export const SearchHomepageBody = () => ( - -
    + + + + + + ); diff --git a/x-pack/plugins/search_homepage/public/components/search_homepage_header.tsx b/x-pack/plugins/search_homepage/public/components/search_homepage_header.tsx index 941655d67cdab..3ea294bb1138c 100644 --- a/x-pack/plugins/search_homepage/public/components/search_homepage_header.tsx +++ b/x-pack/plugins/search_homepage/public/components/search_homepage_header.tsx @@ -8,7 +8,13 @@ import React from 'react'; import { EuiPageTemplate, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -export const SearchHomepageHeader = () => ( +import { EndpointsHeaderAction } from './endpoints_header_action'; + +export interface SearchHomepageHeaderProps { + showEndpointsAPIKeys: boolean; +} + +export const SearchHomepageHeader = ({ showEndpointsAPIKeys }: SearchHomepageHeaderProps) => ( @@ -21,6 +27,6 @@ export const SearchHomepageHeader = () => ( } data-test-subj="search-homepage-header" - rightSideItems={[]} + rightSideItems={[...(showEndpointsAPIKeys ? [] : [])]} /> ); diff --git a/x-pack/plugins/search_homepage/public/components/stack_app.tsx b/x-pack/plugins/search_homepage/public/components/stack_app.tsx index ca18ac7112c09..f570d81e97c3a 100644 --- a/x-pack/plugins/search_homepage/public/components/stack_app.tsx +++ b/x-pack/plugins/search_homepage/public/components/stack_app.tsx @@ -12,7 +12,7 @@ import { SearchHomepageHeader } from './search_homepage_header'; export const App: React.FC = () => { return ( <> - + ); diff --git a/x-pack/plugins/search_homepage/tsconfig.json b/x-pack/plugins/search_homepage/tsconfig.json index 9f084b32f7d3b..91de974fd2914 100644 --- a/x-pack/plugins/search_homepage/tsconfig.json +++ b/x-pack/plugins/search_homepage/tsconfig.json @@ -23,6 +23,7 @@ "@kbn/share-plugin", "@kbn/usage-collection-plugin", "@kbn/config-schema", + "@kbn/cloud", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/search_inference_endpoints/common/translations.ts b/x-pack/plugins/search_inference_endpoints/common/translations.ts index e58829812829b..8171b8bba0254 100644 --- a/x-pack/plugins/search_inference_endpoints/common/translations.ts +++ b/x-pack/plugins/search_inference_endpoints/common/translations.ts @@ -14,6 +14,10 @@ export const INFERENCE_ENDPOINT_LABEL = i18n.translate( } ); +export const CANCEL = i18n.translate('xpack.searchInferenceEndpoints.cancel', { + defaultMessage: 'Cancel', +}); + export const MANAGE_INFERENCE_ENDPOINTS_LABEL = i18n.translate( 'xpack.searchInferenceEndpoints.allInferenceEndpoints.description', { @@ -94,3 +98,63 @@ export const FORBIDDEN_TO_ACCESS_TRAINED_MODELS = i18n.translate( defaultMessage: 'Forbidden to access trained models', } ); + +export const COPY_ID_ACTION_LABEL = i18n.translate( + 'xpack.searchInferenceEndpoints.actions.copyID', + { + defaultMessage: 'Copy endpoint ID', + } +); + +export const COPY_ID_ACTION_SUCCESS = i18n.translate( + 'xpack.searchInferenceEndpoints.actions.copyIDSuccess', + { + defaultMessage: 'Inference endpoint ID copied!', + } +); + +export const ENDPOINT_ADDED_SUCCESS = i18n.translate( + 'xpack.searchInferenceEndpoints.actions.endpointAddedSuccess', + { + defaultMessage: 'Endpoint added', + } +); + +export const ENDPOINT_CREATION_FAILED = i18n.translate( + 'xpack.searchInferenceEndpoints.actions.endpointAddedFailure', + { + defaultMessage: 'Endpoint creation failed', + } +); + +export const ENDPOINT_ADDED_SUCCESS_DESCRIPTION = (endpointId: string) => + i18n.translate('xpack.searchInferenceEndpoints.actions.endpointAddedSuccessDescription', { + defaultMessage: 'The inference endpoint "{endpointId}" was added.', + values: { endpointId }, + }); + +export const DELETE_ACTION_LABEL = i18n.translate( + 'xpack.searchInferenceEndpoints.actions.deleteSingleEndpoint', + { + defaultMessage: 'Delete endpoint', + } +); + +export const ENDPOINT = i18n.translate('xpack.searchInferenceEndpoints.endpoint', { + defaultMessage: 'Endpoint', +}); + +export const SERVICE_PROVIDER = i18n.translate('xpack.searchInferenceEndpoints.serviceProvider', { + defaultMessage: 'Service', +}); + +export const TASK_TYPE = i18n.translate('xpack.searchInferenceEndpoints.taskType', { + defaultMessage: 'Type', +}); + +export const TRAINED_MODELS_STAT_GATHER_FAILED = i18n.translate( + 'xpack.searchInferenceEndpoints.actions.trainedModelsStatGatherFailed', + { + defaultMessage: 'Failed to retrieve trained model statistics', + } +); diff --git a/x-pack/plugins/search_inference_endpoints/common/types.ts b/x-pack/plugins/search_inference_endpoints/common/types.ts index ef29d987309f8..e6529de4e3a64 100644 --- a/x-pack/plugins/search_inference_endpoints/common/types.ts +++ b/x-pack/plugins/search_inference_endpoints/common/types.ts @@ -7,6 +7,7 @@ export enum APIRoutes { GET_INFERENCE_ENDPOINTS = '/internal/inference_endpoints/endpoints', + DELETE_INFERENCE_ENDPOINT = '/internal/inference_endpoint/endpoints/{type}/{id}', } export interface SearchInferenceEndpointsConfigType { diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/azure_ai_studio.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/azure_ai_studio.svg new file mode 100644 index 0000000000000..405e182a10394 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/azure_ai_studio.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/azure_open_ai.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/azure_open_ai.svg new file mode 100644 index 0000000000000..122c0c65af13c --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/azure_open_ai.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/cohere.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/cohere.svg new file mode 100644 index 0000000000000..69953809fec35 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/cohere.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/elastic.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/elastic.svg new file mode 100644 index 0000000000000..e763c2e2f2ab6 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/elastic.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/google_ai_studio.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/google_ai_studio.svg new file mode 100644 index 0000000000000..b6e34ae15c9e4 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/google_ai_studio.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/hugging_face.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/hugging_face.svg new file mode 100644 index 0000000000000..87ac70c5a18f4 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/hugging_face.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/mistral.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/mistral.svg new file mode 100644 index 0000000000000..f62258a327594 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/mistral.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/open_ai.svg b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/open_ai.svg new file mode 100644 index 0000000000000..9ddc8f8fd63b8 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/assets/images/providers/open_ai.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts index 1b7e72149fd43..b3fd13dc5383a 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts @@ -8,8 +8,9 @@ import { SortFieldInferenceEndpoint, QueryParams, - AlInferenceEndpointsTableState, + AllInferenceEndpointsTableState, SortOrder, + FilterOptions, } from './types'; export const DEFAULT_TABLE_ACTIVE_PAGE = 1; @@ -22,6 +23,12 @@ export const DEFAULT_QUERY_PARAMS: QueryParams = { sortOrder: SortOrder.asc, }; -export const DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE: AlInferenceEndpointsTableState = { +export const DEFAULT_FILTER_OPTIONS: FilterOptions = { + provider: [], + type: [], +}; + +export const DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE: AllInferenceEndpointsTableState = { + filterOptions: DEFAULT_FILTER_OPTIONS, queryParams: DEFAULT_QUERY_PARAMS, }; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.test.tsx new file mode 100644 index 0000000000000..87b984c26d3ea --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.test.tsx @@ -0,0 +1,66 @@ +/* + * 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 React from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import { MultiSelectFilter, MultiSelectFilterOption } from './multi_select_filter'; +import '@testing-library/jest-dom/extend-expect'; + +describe('MultiSelectFilter', () => { + const options: MultiSelectFilterOption[] = [ + { key: '1', label: 'Option 1', checked: 'off' }, + { key: '2', label: 'Option 2', checked: 'on' }, + { key: '3', label: 'Option 3', checked: 'off' }, + ]; + + it('should render the filter button with the provided label', () => { + const { getByText } = render( + {}} options={options} buttonLabel="Filter Options" /> + ); + expect(getByText('Filter Options')).toBeInTheDocument(); + }); + + it('should toggle the popover when the filter button is clicked', async () => { + const { getByText, queryByText } = render( + {}} options={options} buttonLabel="Filter Options" /> + ); + fireEvent.click(getByText('Filter Options')); + expect(queryByText('Option 1')).toBeInTheDocument(); + fireEvent.click(getByText('Filter Options')); + await waitFor(() => { + expect(queryByText('Option 1')).not.toBeInTheDocument(); + }); + }); + + it('should render the provided options', async () => { + const { getByText } = render( + {}} options={options} buttonLabel="Filter Options" /> + ); + + fireEvent.click(getByText('Filter Options')); + + await waitFor(() => { + expect(getByText('Option 1')).toBeInTheDocument(); + expect(getByText('Option 2')).toBeInTheDocument(); + expect(getByText('Option 3')).toBeInTheDocument(); + }); + }); + + it('should call the onChange function with the updated options when an option is clicked', async () => { + const onChange = jest.fn(); + const { getByText } = render( + + ); + + fireEvent.click(getByText('Filter Options')); + fireEvent.click(getByText('Option 1')); + + await waitFor(() => { + expect(onChange).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx new file mode 100644 index 0000000000000..84883c4e85432 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx @@ -0,0 +1,108 @@ +/* + * 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 { + EuiFilterButton, + EuiFilterGroup, + EuiPopover, + EuiPopoverTitle, + EuiSelectable, + EuiSpacer, + EuiText, + EuiTextColor, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import React, { useState } from 'react'; +import * as i18n from './translations'; + +export interface MultiSelectFilterOption { + key: string; + label: string; + checked?: 'on' | 'off'; +} + +interface UseFilterParams { + buttonLabel?: string; + onChange: (newOptions: MultiSelectFilterOption[]) => void; + options: MultiSelectFilterOption[]; + renderOption?: (option: MultiSelectFilterOption) => React.ReactNode; + selectedOptionKeys?: string[]; +} + +export const MultiSelectFilter: React.FC = ({ + buttonLabel, + onChange, + options: rawOptions, + selectedOptionKeys = [], + renderOption, +}) => { + const { euiTheme } = useEuiTheme(); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const toggleIsPopoverOpen = () => setIsPopoverOpen((prevValue) => !prevValue); + const options: MultiSelectFilterOption[] = rawOptions.map(({ key, label }) => ({ + label, + key, + checked: selectedOptionKeys.includes(key) ? 'on' : undefined, + })); + + return ( + + 0} + numActiveFilters={selectedOptionKeys.length} + aria-label={buttonLabel} + > + + {buttonLabel} + + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + panelPaddingSize="none" + repositionOnScroll + > + + {(list, search) => ( +
    + {search} +
    + {i18n.OPTIONS(options.length)} +
    + + {list} +
    + )} +
    +
    +
    + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/service_provider_filter.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/service_provider_filter.tsx new file mode 100644 index 0000000000000..3d7f9568428ef --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/service_provider_filter.tsx @@ -0,0 +1,43 @@ +/* + * 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 React from 'react'; +import { SERVICE_PROVIDERS } from '../render_table_columns/render_service_provider/service_provider'; +import type { FilterOptions, ServiceProviderKeys } from '../types'; +import { MultiSelectFilter, MultiSelectFilterOption } from './multi_select_filter'; +import * as i18n from './translations'; + +interface Props { + optionKeys: ServiceProviderKeys[]; + onChange: (newFilterOptions: Partial) => void; +} + +const options = Object.entries(SERVICE_PROVIDERS).map(([key, { name }]) => ({ + key, + label: name, +})); + +export const ServiceProviderFilter: React.FC = ({ optionKeys, onChange }) => { + const filterId: string = 'provider'; + const onSystemFilterChange = (newOptions: MultiSelectFilterOption[]) => { + onChange({ + [filterId]: newOptions + .filter((option) => option.checked === 'on') + .map((option) => option.key), + }); + }; + + return ( + option.label} + selectedOptionKeys={optionKeys} + /> + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/task_type_filter.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/task_type_filter.tsx new file mode 100644 index 0000000000000..389757b833db6 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/task_type_filter.tsx @@ -0,0 +1,42 @@ +/* + * 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 React from 'react'; +import { FilterOptions, TaskTypes } from '../types'; +import { MultiSelectFilter, MultiSelectFilterOption } from './multi_select_filter'; +import * as i18n from './translations'; + +interface Props { + optionKeys: TaskTypes[]; + onChange: (newFilterOptions: Partial) => void; +} + +const options = Object.values(TaskTypes).map((option) => ({ + key: option, + label: option, +})); + +export const TaskTypeFilter: React.FC = ({ optionKeys, onChange }) => { + const filterId: string = 'type'; + const onSystemFilterChange = (newOptions: MultiSelectFilterOption[]) => { + onChange({ + [filterId]: newOptions + .filter((option) => option.checked === 'on') + .map((option) => option.key), + }); + }; + + return ( + option.label} + selectedOptionKeys={optionKeys} + /> + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/translations.ts new file mode 100644 index 0000000000000..f373e8f5d5a46 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/translations.ts @@ -0,0 +1,22 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export { SERVICE_PROVIDER, TASK_TYPE } from '../../../../common/translations'; + +export const EMPTY_FILTER_MESSAGE = i18n.translate( + 'xpack.searchInferenceEndpoints.filter.emptyMessage', + { + defaultMessage: 'No options', + } +); +export const OPTIONS = (totalCount: number) => + i18n.translate('xpack.searchInferenceEndpoints.filter.options', { + defaultMessage: '{totalCount, plural, one {# option} other {# options}}', + values: { totalCount }, + }); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.test.tsx new file mode 100644 index 0000000000000..1445e0c41c574 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.test.tsx @@ -0,0 +1,73 @@ +/* + * 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 { renderReactTestingLibraryWithI18n as render } from '@kbn/test-jest-helpers'; +import React from 'react'; +import { useKibana } from '../../../../../../hooks/use_kibana'; +import { useCopyIDAction } from './use_copy_id_action'; + +const mockInferenceEndpoint = { + deployment: 'not_applicable', + endpoint: { + model_id: 'hugging-face-embeddings', + task_type: 'text_embedding', + service: 'hugging_face', + service_settings: { + dimensions: 768, + rate_limit: { + requests_per_minute: 3000, + }, + }, + task_settings: {}, + }, + provider: 'hugging_face', + type: 'text_embedding', +} as any; + +Object.defineProperty(navigator, 'clipboard', { + value: { + writeText: jest.fn().mockResolvedValue(undefined), + }, + configurable: true, +}); + +const mockOnActionSuccess = jest.fn(); + +jest.mock('../../../../../../hooks/use_kibana', () => ({ + useKibana: jest.fn(), +})); + +const addSuccess = jest.fn(); + +(useKibana as jest.Mock).mockImplementation(() => ({ + services: { + notifications: { + toasts: { + addSuccess, + }, + }, + }, +})); + +describe('useCopyIDAction hook', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders the label with correct text', () => { + const TestComponent = () => { + const { getAction } = useCopyIDAction({ onActionSuccess: mockOnActionSuccess }); + const action = getAction(mockInferenceEndpoint); + return
    {action}
    ; + }; + + const { getByTestId } = render(); + const labelElement = getByTestId('inference-endpoints-action-copy-id-label'); + + expect(labelElement).toHaveTextContent('Copy endpoint ID'); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.tsx new file mode 100644 index 0000000000000..b43b308af068a --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/copy_id/use_copy_id_action.tsx @@ -0,0 +1,44 @@ +/* + * 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 { EuiContextMenuItem, EuiCopy, EuiIcon } from '@elastic/eui'; +import React from 'react'; +import * as i18n from '../../../../../../../common/translations'; +import { useKibana } from '../../../../../../hooks/use_kibana'; +import { InferenceEndpointUI } from '../../../../types'; +import { UseCopyIDActionProps } from '../types'; + +export const useCopyIDAction = ({ onActionSuccess }: UseCopyIDActionProps) => { + const { + services: { notifications }, + } = useKibana(); + const toasts = notifications?.toasts; + + const getAction = (inferenceEndpoint: InferenceEndpointUI) => { + return ( + + {(copy) => ( + } + onClick={() => { + copy(); + onActionSuccess(); + toasts?.addSuccess({ title: i18n.COPY_ID_ACTION_SUCCESS }); + }} + size="s" + > + {i18n.COPY_ID_ACTION_LABEL} + + )} + + ); + }; + + return { getAction }; +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.test.tsx new file mode 100644 index 0000000000000..e6f4594e2d0cd --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.test.tsx @@ -0,0 +1,41 @@ +/* + * 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 { render, fireEvent, screen } from '@testing-library/react'; +import React from 'react'; +import { ConfirmDeleteEndpointModal } from '.'; +import * as i18n from './translations'; + +describe('ConfirmDeleteEndpointModal', () => { + const mockOnCancel = jest.fn(); + const mockOnConfirm = jest.fn(); + + beforeEach(() => { + render(); + }); + + it('renders the modal with correct texts', () => { + expect(screen.getByText(i18n.DELETE_TITLE)).toBeInTheDocument(); + expect(screen.getByText(i18n.CONFIRM_DELETE_WARNING)).toBeInTheDocument(); + expect(screen.getByText(i18n.CANCEL)).toBeInTheDocument(); + expect(screen.getByText(i18n.DELETE_ACTION_LABEL)).toBeInTheDocument(); + }); + + it('calls onCancel when the cancel button is clicked', () => { + fireEvent.click(screen.getByText(i18n.CANCEL)); + expect(mockOnCancel).toHaveBeenCalled(); + }); + + it('calls onConfirm when the delete button is clicked', () => { + fireEvent.click(screen.getByText(i18n.DELETE_ACTION_LABEL)); + expect(mockOnConfirm).toHaveBeenCalled(); + }); + + it('has the delete button focused by default', () => { + expect(document.activeElement).toHaveTextContent(i18n.DELETE_ACTION_LABEL); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx new file mode 100644 index 0000000000000..e650192a66dc7 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx @@ -0,0 +1,34 @@ +/* + * 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 React from 'react'; +import { EuiConfirmModal } from '@elastic/eui'; +import * as i18n from './translations'; + +interface ConfirmDeleteEndpointModalProps { + onCancel: () => void; + onConfirm: () => void; +} + +export const ConfirmDeleteEndpointModal: React.FC = ({ + onCancel, + onConfirm, +}) => { + return ( + + {i18n.CONFIRM_DELETE_WARNING} + + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts new file mode 100644 index 0000000000000..6b2dff7d8b8f1 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts @@ -0,0 +1,24 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +export * from '../../../../../../../../common/translations'; + +export const DELETE_TITLE = i18n.translate( + 'xpack.searchInferenceEndpoints.confirmDeleteEndpoint.title', + { + defaultMessage: 'Delete inference endpoint', + } +); + +export const CONFIRM_DELETE_WARNING = i18n.translate( + 'xpack.searchInferenceEndpoints.confirmDeleteEndpoint.confirmQuestion', + { + defaultMessage: + 'Deleting an active endpoint will cause operations targeting associated semantic_text fields and inference pipelines to fail.', + } +); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/use_delete_action.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/use_delete_action.tsx new file mode 100644 index 0000000000000..1a2e5cdb5b51f --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/use_delete_action.tsx @@ -0,0 +1,55 @@ +/* + * 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 { EuiContextMenuItem, EuiIcon } from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import * as i18n from '../../../../../../../common/translations'; +import { useDeleteEndpoint } from '../../../../../../hooks/use_delete_endpoint'; +import { InferenceEndpointUI } from '../../../../types'; +import type { UseActionProps } from '../types'; + +export const useDeleteAction = ({ onActionSuccess }: UseActionProps) => { + const [isModalVisible, setIsModalVisible] = useState(false); + const [endpointToBeDeleted, setEndpointToBeDeleted] = useState(null); + const onCloseModal = useCallback(() => setIsModalVisible(false), []); + const openModal = useCallback( + (selectedEndpoint: InferenceEndpointUI) => { + onActionSuccess(); + setIsModalVisible(true); + setEndpointToBeDeleted(selectedEndpoint); + }, + [onActionSuccess] + ); + + const { mutate: deleteEndpoint } = useDeleteEndpoint(); + + const onConfirmDeletion = useCallback(() => { + onCloseModal(); + if (!endpointToBeDeleted) { + return; + } + + deleteEndpoint({ + type: endpointToBeDeleted.type, + id: endpointToBeDeleted.endpoint.model_id, + }); + }, [deleteEndpoint, onCloseModal, endpointToBeDeleted]); + + const getAction = (selectedEndpoint: InferenceEndpointUI) => { + return ( + } + onClick={() => openModal(selectedEndpoint)} + > + {i18n.DELETE_ACTION_LABEL} + + ); + }; + + return { getAction, isModalVisible, onConfirmDeletion, onCloseModal }; +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/types.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/types.ts new file mode 100644 index 0000000000000..a80ccae703b7a --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/types.ts @@ -0,0 +1,12 @@ +/* + * 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 UseActionProps { + onActionSuccess: () => void; +} + +export type UseCopyIDActionProps = Pick; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/use_actions.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/use_actions.tsx new file mode 100644 index 0000000000000..f76d147c68646 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/use_actions.tsx @@ -0,0 +1,79 @@ +/* + * 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 type { EuiTableComputedColumnType } from '@elastic/eui'; +import { EuiButtonIcon, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import { InferenceEndpointUI } from '../../types'; +import { useCopyIDAction } from './actions/copy_id/use_copy_id_action'; +import { ConfirmDeleteEndpointModal } from './actions/delete/confirm_delete_endpoint'; +import { useDeleteAction } from './actions/delete/use_delete_action'; + +export const ActionColumn: React.FC<{ interfaceEndpoint: InferenceEndpointUI }> = ({ + interfaceEndpoint, +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const tooglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); + const closePopover = useCallback(() => setIsPopoverOpen(false), []); + + const copyIDAction = useCopyIDAction({ + onActionSuccess: closePopover, + }); + + const deleteAction = useDeleteAction({ + onActionSuccess: closePopover, + }); + + const items = [ + copyIDAction.getAction(interfaceEndpoint), + deleteAction.getAction(interfaceEndpoint), + ]; + + return ( + <> + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + {deleteAction.isModalVisible ? ( + + ) : null} + + ); +}; + +interface UseBulkActionsReturnValue { + actions: EuiTableComputedColumnType; +} + +export const useActions = (): UseBulkActionsReturnValue => { + return { + actions: { + align: 'right', + render: (interfaceEndpoint: InferenceEndpointUI) => { + return ; + }, + width: '165px', + }, + }; +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.test.tsx new file mode 100644 index 0000000000000..59414cd6c90ef --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.test.tsx @@ -0,0 +1,27 @@ +/* + * 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 { render, screen } from '@testing-library/react'; +import React from 'react'; +import { DeploymentStatus } from './deployment_status'; +import { DeploymentStatusEnum } from '../../types'; + +describe('DeploymentStatus component', () => { + it.each([[DeploymentStatusEnum.deployed, DeploymentStatusEnum.notDeployed]])( + 'renders with %s status, expects %s color, and correct data-test-subj attribute', + (status) => { + render(); + const healthComponent = screen.getByTestId(`table-column-deployment-${status}`); + expect(healthComponent).toBeInTheDocument(); + } + ); + + it('does not render when status is notApplicable', () => { + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.tsx new file mode 100644 index 0000000000000..61e6e620d2485 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/deployment_status.tsx @@ -0,0 +1,52 @@ +/* + * 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 React from 'react'; +import { EuiIcon, EuiToolTip } from '@elastic/eui'; +import { DeploymentStatusEnum } from '../../types'; +import * as i18n from './translations'; + +interface DeploymentStatusProps { + status: DeploymentStatusEnum; +} + +export const DeploymentStatus: React.FC = ({ status }) => { + if (status === DeploymentStatusEnum.notApplicable) { + return null; + } + + let statusColor: string; + let type: string; + let tooltip: string; + + switch (status) { + case DeploymentStatusEnum.deployed: + statusColor = 'success'; + type = 'dot'; + tooltip = i18n.MODEL_DEPLOYED; + break; + case DeploymentStatusEnum.notDeployed: + statusColor = 'warning'; + type = 'warning'; + tooltip = i18n.MODEL_NOT_DEPLOYED; + break; + case DeploymentStatusEnum.notDeployable: + statusColor = 'danger'; + type = 'dot'; + tooltip = i18n.MODEL_FAILED_TO_BE_DEPLOYED; + } + + return ( + + + + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/translations.ts new file mode 100644 index 0000000000000..9a5448f5bf0d2 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_deployment_status/translations.ts @@ -0,0 +1,29 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const MODEL_DEPLOYED = i18n.translate( + 'xpack.searchInferenceEndpoints.deploymentStatus.tooltip.modelDeployed', + { + defaultMessage: 'Model is deployed', + } +); + +export const MODEL_NOT_DEPLOYED = i18n.translate( + 'xpack.searchInferenceEndpoints.deploymentStatus.tooltip.modelNotDeployed', + { + defaultMessage: 'Model is not deployed', + } +); + +export const MODEL_FAILED_TO_BE_DEPLOYED = i18n.translate( + 'xpack.searchInferenceEndpoints.deploymentStatus.tooltip.modelFailedToBeDeployed', + { + defaultMessage: 'Model can not be deployed', + } +); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.test.tsx new file mode 100644 index 0000000000000..c92c01240425c --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.test.tsx @@ -0,0 +1,257 @@ +/* + * 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 { render, screen } from '@testing-library/react'; +import React from 'react'; +import { EndpointInfo } from './endpoint_info'; + +describe('RenderEndpoint component tests', () => { + describe('with cohere service', () => { + const mockEndpoint = { + model_id: 'cohere-2', + service: 'cohere', + service_settings: { + similarity: 'cosine', + dimensions: 384, + model_id: 'embed-english-light-v3.0', + rate_limit: { + requests_per_minute: 10000, + }, + embedding_type: 'byte', + }, + task_settings: {}, + } as any; + + it('renders the component with endpoint details for Cohere service', () => { + render(); + + expect(screen.getByText('cohere-2')).toBeInTheDocument(); + expect(screen.getByText('byte')).toBeInTheDocument(); + expect(screen.getByText('embed-english-light-v3.0')).toBeInTheDocument(); + }); + + it('does not render model_id badge if serviceSettings.model_id is not provided for Cohere service', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: { ...mockEndpoint.service_settings, model_id: undefined }, + }; + render(); + + expect(screen.queryByText('embed-english-light-v3.0')).not.toBeInTheDocument(); + }); + + it('renders only model_id if other settings are not provided for Cohere service', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: { model_id: 'embed-english-light-v3.0' }, + }; + render(); + + expect(screen.getByText('embed-english-light-v3.0')).toBeInTheDocument(); + expect(screen.queryByText(',')).not.toBeInTheDocument(); + }); + }); + + describe('with elasticsearch service', () => { + const mockEndpoint = { + model_id: 'model-123', + service: 'elasticsearch', + service_settings: { + num_allocations: 5, + num_threads: 10, + model_id: 'settings-model-123', + }, + } as any; + + it('renders the component with endpoint model_id and model settings', () => { + render(); + + expect(screen.getByText('model-123')).toBeInTheDocument(); + expect(screen.getByText('settings-model-123')).toBeInTheDocument(); + expect(screen.getByText('Threads: 10 | Allocations: 5')).toBeInTheDocument(); + }); + + it('renders the component with only model_id if num_threads and num_allocations are not provided', () => { + const modifiedSettings = { + ...mockEndpoint.service_settings, + num_threads: undefined, + num_allocations: undefined, + }; + const modifiedEndpoint = { ...mockEndpoint, service_settings: modifiedSettings }; + render(); + + expect(screen.getByText('model-123')).toBeInTheDocument(); + expect(screen.getByText('settings-model-123')).toBeInTheDocument(); + expect(screen.queryByText('Threads: 10 | Allocations: 5')).not.toBeInTheDocument(); + }); + }); + + describe('with azureaistudio service', () => { + const mockEndpoint = { + model_id: 'azure-ai-1', + service: 'azureaistudio', + service_settings: { + target: 'westus', + provider: 'microsoft_phi', + endpoint_type: 'realtime', + }, + } as any; + + it('renders the component with endpoint details', () => { + render(); + + expect(screen.getByText('azure-ai-1')).toBeInTheDocument(); + expect(screen.getByText('microsoft_phi, realtime, westus')).toBeInTheDocument(); + }); + + it('renders correctly when some service settings are missing', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: { target: 'westus', provider: 'microsoft_phi' }, + }; + render(); + + expect(screen.getByText('microsoft_phi, westus')).toBeInTheDocument(); + }); + + it('does not render a comma when only one service setting is provided', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: { target: 'westus' }, + }; + render(); + + expect(screen.getByText('westus')).toBeInTheDocument(); + expect(screen.queryByText(',')).not.toBeInTheDocument(); + }); + + it('renders nothing related to service settings when all are missing', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: {}, + }; + render(); + + expect(screen.getByText('azure-ai-1')).toBeInTheDocument(); + expect(screen.queryByText('westus')).not.toBeInTheDocument(); + expect(screen.queryByText('microsoft_phi')).not.toBeInTheDocument(); + expect(screen.queryByText('realtime')).not.toBeInTheDocument(); + }); + }); + + describe('with azureopenai service', () => { + const mockEndpoint = { + model_id: 'azure-openai-1', + service: 'azureopenai', + service_settings: { + resource_name: 'resource-xyz', + deployment_id: 'deployment-123', + api_version: 'v1', + }, + } as any; + + it('renders the component with all required endpoint details', () => { + render(); + + expect(screen.getByText('azure-openai-1')).toBeInTheDocument(); + expect(screen.getByText('resource-xyz, deployment-123, v1')).toBeInTheDocument(); + }); + }); + + describe('with mistral service', () => { + const mockEndpoint = { + model_id: 'mistral-ai-1', + service: 'mistral', + service_settings: { + model: 'model-xyz', + max_input_tokens: 512, + rate_limit: { + requests_per_minute: 1000, + }, + }, + } as any; + + it('renders the component with endpoint details', () => { + render(); + + expect(screen.getByText('mistral-ai-1')).toBeInTheDocument(); + expect(screen.getByText('model-xyz')).toBeInTheDocument(); + expect(screen.getByText('max_input_tokens: 512, rate_limit: 1000')).toBeInTheDocument(); + }); + + it('renders correctly when some service settings are missing', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: { + model: 'model-xyz', + max_input_tokens: 512, + }, + }; + render(); + + expect(screen.getByText('max_input_tokens: 512')).toBeInTheDocument(); + }); + + it('does not render a comma when only one service setting is provided', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: { model: 'model-xyz' }, + }; + render(); + + expect(screen.getByText('model-xyz')).toBeInTheDocument(); + expect(screen.queryByText(',')).not.toBeInTheDocument(); + }); + + it('renders nothing related to service settings when all are missing', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: {}, + }; + render(); + + expect(screen.getByText('mistral-ai-1')).toBeInTheDocument(); + expect(screen.queryByText('model-xyz')).not.toBeInTheDocument(); + expect(screen.queryByText('max_input_tokens: 512')).not.toBeInTheDocument(); + expect(screen.queryByText('rate_limit: 1000')).not.toBeInTheDocument(); + }); + }); + + describe('with googleaistudio service', () => { + const mockEndpoint = { + model_id: 'google-ai-1', + service: 'googleaistudio', + service_settings: { + model_id: 'model-abc', + rate_limit: { + requests_per_minute: 500, + }, + }, + } as any; + + it('renders the component with endpoint details', () => { + render(); + + expect(screen.getByText('model-abc')).toBeInTheDocument(); + expect(screen.getByText('rate_limit: 500')).toBeInTheDocument(); + }); + + it('renders correctly when rate limit is missing', () => { + const modifiedEndpoint = { + ...mockEndpoint, + service_settings: { + model_id: 'model-abc', + }, + }; + + render(); + + expect(screen.getByText('model-abc')).toBeInTheDocument(); + expect(screen.queryByText('Rate limit:')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx new file mode 100644 index 0000000000000..ae482b609346f --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/endpoint_info.tsx @@ -0,0 +1,164 @@ +/* + * 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 React from 'react'; +import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { ServiceProviderKeys } from '../../types'; +import { ModelBadge } from './model_badge'; +import * as i18n from './translations'; + +export interface EndpointInfoProps { + endpoint: InferenceAPIConfigResponse; +} + +export const EndpointInfo: React.FC = ({ endpoint }) => { + return ( + + + {endpoint.model_id} + + + + + + ); +}; + +export const EndpointModelInfo: React.FC = ({ endpoint }) => { + const serviceSettings = endpoint.service_settings; + const modelId = + 'model_id' in serviceSettings + ? serviceSettings.model_id + : 'model' in serviceSettings + ? serviceSettings.model + : undefined; + + return ( + + {modelId && ( + + + + )} + + + {endpointModelAtrributes(endpoint)} + + + + ); +}; + +function endpointModelAtrributes(endpoint: InferenceAPIConfigResponse) { + switch (endpoint.service) { + case ServiceProviderKeys.elser: + case ServiceProviderKeys.elasticsearch: + return elasticsearchAttributes(endpoint); + case ServiceProviderKeys.cohere: + return cohereAttributes(endpoint); + case ServiceProviderKeys.hugging_face: + return huggingFaceAttributes(endpoint); + case ServiceProviderKeys.openai: + return openAIAttributes(endpoint); + case ServiceProviderKeys.azureaistudio: + return azureOpenAIStudioAttributes(endpoint); + case ServiceProviderKeys.azureopenai: + return azureOpenAIAttributes(endpoint); + case ServiceProviderKeys.mistral: + return mistralAttributes(endpoint); + case ServiceProviderKeys.googleaistudio: + return googleAIStudioAttributes(endpoint); + default: + return null; + } +} + +function elasticsearchAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + + const numAllocations = + 'num_allocations' in serviceSettings ? serviceSettings.num_allocations : undefined; + const numThreads = 'num_threads' in serviceSettings ? serviceSettings.num_threads : undefined; + + return `${numThreads ? i18n.THREADS(numThreads) : ''}${ + numThreads && numAllocations ? ' | ' : '' + }${numAllocations ? i18n.ALLOCATIONS(numAllocations) : ''}`; +} + +function cohereAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + const embeddingType = + 'embedding_type' in serviceSettings ? serviceSettings.embedding_type : undefined; + + const taskSettings = endpoint.task_settings; + const inputType = 'input_type' in taskSettings ? taskSettings.input_type : undefined; + const truncate = 'truncate' in taskSettings ? taskSettings.truncate : undefined; + + return [embeddingType, inputType, truncate && `truncate: ${truncate}`].filter(Boolean).join(', '); +} + +function huggingFaceAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + const url = 'url' in serviceSettings ? serviceSettings.url : null; + + return url; +} + +function openAIAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + const url = 'url' in serviceSettings ? serviceSettings.url : null; + + return url; +} + +function azureOpenAIStudioAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + const provider = 'provider' in serviceSettings ? serviceSettings.provider : undefined; + const endpointType = + 'endpoint_type' in serviceSettings ? serviceSettings.endpoint_type : undefined; + const target = 'target' in serviceSettings ? serviceSettings.target : undefined; + + return [provider, endpointType, target].filter(Boolean).join(', '); +} + +function azureOpenAIAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + + const resourceName = + 'resource_name' in serviceSettings ? serviceSettings.resource_name : undefined; + const deploymentId = + 'deployment_id' in serviceSettings ? serviceSettings.deployment_id : undefined; + const apiVersion = 'api_version' in serviceSettings ? serviceSettings.api_version : undefined; + + return [resourceName, deploymentId, apiVersion].filter(Boolean).join(', '); +} + +function mistralAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + + const maxInputTokens = + 'max_input_tokens' in serviceSettings ? serviceSettings.max_input_tokens : undefined; + const rateLimit = + 'rate_limit' in serviceSettings ? serviceSettings.rate_limit.requests_per_minute : undefined; + + return [ + maxInputTokens && `max_input_tokens: ${maxInputTokens}`, + rateLimit && `rate_limit: ${rateLimit}`, + ] + .filter(Boolean) + .join(', '); +} + +function googleAIStudioAttributes(endpoint: InferenceAPIConfigResponse) { + const serviceSettings = endpoint.service_settings; + + const rateLimit = + 'rate_limit' in serviceSettings ? serviceSettings.rate_limit.requests_per_minute : undefined; + + return rateLimit && `rate_limit: ${rateLimit}`; +} diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/model_badge.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/model_badge.tsx new file mode 100644 index 0000000000000..e4b241abd8199 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/model_badge.tsx @@ -0,0 +1,21 @@ +/* + * 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 React from 'react'; +import { EuiBadge, useEuiTheme } from '@elastic/eui'; + +interface ModelBadgeProps { + model?: string; +} + +export const ModelBadge: React.FC = ({ model }) => { + const { euiTheme } = useEuiTheme(); + + if (!model) return null; + + return {model}; +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/translations.ts new file mode 100644 index 0000000000000..52705999e8b44 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_endpoint/translations.ts @@ -0,0 +1,20 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const THREADS = (numThreads: number) => + i18n.translate('xpack.searchInferenceEndpoints.elasticsearch.threads', { + defaultMessage: 'Threads: {numThreads}', + values: { numThreads }, + }); + +export const ALLOCATIONS = (numAllocations: number) => + i18n.translate('xpack.searchInferenceEndpoints.elasticsearch.allocations', { + defaultMessage: 'Allocations: {numAllocations}', + values: { numAllocations }, + }); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.test.tsx new file mode 100644 index 0000000000000..a592569abb0aa --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.test.tsx @@ -0,0 +1,32 @@ +/* + * 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 { render, screen } from '@testing-library/react'; +import React from 'react'; +import { ServiceProvider } from './service_provider'; +import { ServiceProviderKeys } from '../../types'; + +jest.mock('../../../../assets/images/providers/elastic.svg', () => 'elasticIcon.svg'); +jest.mock('../../../../assets/images/providers/hugging_face.svg', () => 'huggingFaceIcon.svg'); +jest.mock('../../../../assets/images/providers/cohere.svg', () => 'cohereIcon.svg'); +jest.mock('../../../../assets/images/providers/open_ai.svg', () => 'openAIIcon.svg'); + +describe('ServiceProvider component', () => { + it('renders Hugging Face icon and name when providerKey is hugging_face', () => { + render(); + expect(screen.getByText('Hugging Face')).toBeInTheDocument(); + const icon = screen.getByTestId('table-column-service-provider-hugging_face'); + expect(icon).toBeInTheDocument(); + }); + + it('renders Open AI icon and name when providerKey is openai', () => { + render(); + expect(screen.getByText('OpenAI')).toBeInTheDocument(); + const icon = screen.getByTestId('table-column-service-provider-openai'); + expect(icon).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.tsx new file mode 100644 index 0000000000000..c4f09213158ce --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_service_provider/service_provider.tsx @@ -0,0 +1,83 @@ +/* + * 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 { EuiIcon } from '@elastic/eui'; +import React from 'react'; +import elasticIcon from '../../../../assets/images/providers/elastic.svg'; +import huggingFaceIcon from '../../../../assets/images/providers/hugging_face.svg'; +import cohereIcon from '../../../../assets/images/providers/cohere.svg'; +import openAIIcon from '../../../../assets/images/providers/open_ai.svg'; +import azureAIStudioIcon from '../../../../assets/images/providers/azure_ai_studio.svg'; +import azureOpenAIIcon from '../../../../assets/images/providers/azure_open_ai.svg'; +import googleAIStudioIcon from '../../../../assets/images/providers/google_ai_studio.svg'; +import mistralIcon from '../../../../assets/images/providers/mistral.svg'; +import { ServiceProviderKeys } from '../../types'; + +interface ServiceProviderProps { + providerKey: ServiceProviderKeys; +} + +interface ServiceProviderRecord { + icon: string; + name: string; +} + +export const SERVICE_PROVIDERS: Record = { + [ServiceProviderKeys.azureaistudio]: { + icon: azureAIStudioIcon, + name: 'Azure AI Studio', + }, + [ServiceProviderKeys.azureopenai]: { + icon: azureOpenAIIcon, + name: 'Azure OpenAI', + }, + [ServiceProviderKeys.cohere]: { + icon: cohereIcon, + name: 'Cohere', + }, + [ServiceProviderKeys.elasticsearch]: { + icon: elasticIcon, + name: 'Elasticsearch', + }, + [ServiceProviderKeys.elser]: { + icon: elasticIcon, + name: 'ELSER', + }, + [ServiceProviderKeys.googleaistudio]: { + icon: googleAIStudioIcon, + name: 'Google AI Studio', + }, + [ServiceProviderKeys.hugging_face]: { + icon: huggingFaceIcon, + name: 'Hugging Face', + }, + [ServiceProviderKeys.mistral]: { + icon: mistralIcon, + name: 'Mistral', + }, + [ServiceProviderKeys.openai]: { + icon: openAIIcon, + name: 'OpenAI', + }, +}; + +export const ServiceProvider: React.FC = ({ providerKey }) => { + const provider = SERVICE_PROVIDERS[providerKey]; + + return provider ? ( + <> + + {provider.name} + + ) : ( + {providerKey} + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_task_type/task_type.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_task_type/task_type.test.tsx new file mode 100644 index 0000000000000..c81ce80a619e5 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_task_type/task_type.test.tsx @@ -0,0 +1,29 @@ +/* + * 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 { render, screen } from '@testing-library/react'; +import React from 'react'; +import { TaskType } from './task_type'; +import { TaskTypes } from '../../types'; + +describe('TaskType component', () => { + it.each([ + [TaskTypes.completion, 'completion'], + [TaskTypes.sparse_embedding, 'sparse_embedding'], + [TaskTypes.text_embedding, 'text_embedding'], + ])('renders the task type badge for %s', (taskType, expected) => { + render(); + const badge = screen.getByTestId(`table-column-task-type-${taskType}`); + expect(badge).toBeInTheDocument(); + expect(badge).toHaveTextContent(expected); + }); + + it('returns null when type is null', () => { + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_task_type/task_type.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_task_type/task_type.tsx new file mode 100644 index 0000000000000..c294255961748 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_task_type/task_type.tsx @@ -0,0 +1,26 @@ +/* + * 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 { EuiBadge } from '@elastic/eui'; +import React from 'react'; +import { TaskTypes } from '../../types'; + +interface TaskTypeProps { + type?: TaskTypes; +} + +export const TaskType: React.FC = ({ type }) => { + if (type != null) { + return ( + + {type} + + ); + } + + return null; +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/table_columns.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/table_columns.tsx new file mode 100644 index 0000000000000..caca4449e02fa --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/table_columns.tsx @@ -0,0 +1,79 @@ +/* + * 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 { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; +import React from 'react'; +import type { HorizontalAlignment } from '@elastic/eui'; +import * as i18n from '../../../../common/translations'; +import { useActions } from './render_actions/use_actions'; +import { EndpointInfo } from './render_endpoint/endpoint_info'; +import { ServiceProvider } from './render_service_provider/service_provider'; +import { TaskType } from './render_task_type/task_type'; +import { DeploymentStatus } from './render_deployment_status/deployment_status'; +import { DeploymentStatusEnum, ServiceProviderKeys, TaskTypes } from '../types'; + +export const useTableColumns = () => { + const { actions } = useActions(); + const deploymentAlignment: HorizontalAlignment = 'center'; + + const TABLE_COLUMNS = [ + { + field: 'deployment', + name: '', + render: (deployment: DeploymentStatusEnum) => { + if (deployment != null) { + return ; + } + + return null; + }, + width: '64px', + align: deploymentAlignment, + }, + { + field: 'endpoint', + name: i18n.ENDPOINT, + render: (endpoint: InferenceAPIConfigResponse) => { + if (endpoint != null) { + return ; + } + + return null; + }, + sortable: true, + }, + { + field: 'provider', + name: i18n.SERVICE_PROVIDER, + render: (provider: ServiceProviderKeys) => { + if (provider != null) { + return ; + } + + return null; + }, + sortable: false, + width: '265px', + }, + { + field: 'type', + name: i18n.TASK_TYPE, + render: (type: TaskTypes) => { + if (type != null) { + return ; + } + + return null; + }, + sortable: false, + width: '265px', + }, + actions, + ]; + + return TABLE_COLUMNS; +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.test.tsx new file mode 100644 index 0000000000000..b7d1dbcacfd71 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.test.tsx @@ -0,0 +1,30 @@ +/* + * 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 { render, screen, fireEvent } from '@testing-library/react'; +import { TableSearch } from './table_search'; +import React from 'react'; + +describe('TableSearchComponent', () => { + const mockSetSearchKey = jest.fn(); + + it('renders correctly', () => { + render(); + expect(screen.getByRole('searchbox')).toBeInTheDocument(); + }); + + it('input value matches searchKey prop', () => { + render(); + expect(screen.getByRole('searchbox')).toHaveValue('test'); + }); + + it('calls setSearchKey on input change', () => { + render(); + fireEvent.change(screen.getByRole('searchbox'), { target: { value: 'new search' } }); + expect(mockSetSearchKey).toHaveBeenCalledWith('new search'); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx new file mode 100644 index 0000000000000..086dcb9656d83 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx @@ -0,0 +1,34 @@ +/* + * 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 { EuiFieldSearch } from '@elastic/eui'; +import React, { useCallback } from 'react'; + +interface TableSearchComponentProps { + searchKey: string; + setSearchKey: React.Dispatch>; +} + +export const TableSearch: React.FC = ({ searchKey, setSearchKey }) => { + const onSearch = useCallback( + (newSearch) => { + const trimSearch = newSearch.trim(); + setSearchKey(trimSearch); + }, + [setSearchKey] + ); + + return ( + setSearchKey(e.target.value)} + onSearch={onSearch} + value={searchKey} + /> + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/table_columns.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/table_columns.ts deleted file mode 100644 index 39e957f908684..0000000000000 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/table_columns.ts +++ /dev/null @@ -1,35 +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 { i18n } from '@kbn/i18n'; - -export const TABLE_COLUMNS = [ - { - field: 'endpoint', - name: i18n.translate('xpack.searchInferenceEndpoints.inferenceEndpoints.table.endpoint', { - defaultMessage: 'Endpoint', - }), - sortable: true, - width: '50%', - }, - { - field: 'provider', - name: i18n.translate('xpack.searchInferenceEndpoints.inferenceEndpoints.table.provider', { - defaultMessage: 'Provider', - }), - sortable: false, - width: '110px', - }, - { - field: 'type', - name: i18n.translate('xpack.searchInferenceEndpoints.inferenceEndpoints.table.type', { - defaultMessage: 'Type', - }), - sortable: false, - width: '90px', - }, -]; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx index 091ff8270dd63..91a2ea959fdec 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.test.tsx @@ -36,6 +36,12 @@ const inferenceEndpoints = [ }, ] as InferenceAPIConfigResponse[]; +jest.mock('../../hooks/use_delete_endpoint', () => ({ + useDeleteEndpoint: () => ({ + mutate: jest.fn().mockImplementation(() => Promise.resolve()), // Mock implementation of the mutate function + }), +})); + describe('When the tabular page is loaded', () => { beforeEach(() => { render(); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx index 2fb84dc4b99de..ec19ad49ad477 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/tabular_page.tsx @@ -5,28 +5,87 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; +import * as i18n from '../../../common/translations'; import { useTableData } from '../../hooks/use_table_data'; +import { FilterOptions } from './types'; + +import { DeploymentStatusEnum } from './types'; import { useAllInferenceEndpointsState } from '../../hooks/use_all_inference_endpoints_state'; import { EndpointsTable } from './endpoints_table'; -import { TABLE_COLUMNS } from './table_columns'; +import { ServiceProviderFilter } from './filter/service_provider_filter'; +import { TaskTypeFilter } from './filter/task_type_filter'; +import { TableSearch } from './search/table_search'; +import { useTableColumns } from './render_table_columns/table_columns'; +import { useKibana } from '../../hooks/use_kibana'; interface TabularPageProps { inferenceEndpoints: InferenceAPIConfigResponse[]; } export const TabularPage: React.FC = ({ inferenceEndpoints }) => { - const { queryParams, setQueryParams } = useAllInferenceEndpointsState(); + const [searchKey, setSearchKey] = React.useState(''); + const [deploymentStatus, setDeploymentStatus] = React.useState< + Record + >({}); + const { queryParams, setQueryParams, filterOptions, setFilterOptions } = + useAllInferenceEndpointsState(); + + const { + services: { ml, notifications }, + } = useKibana(); + + const onFilterChangedCallback = useCallback( + (newFilterOptions: Partial) => { + setFilterOptions(newFilterOptions); + }, + [setFilterOptions] + ); + + useEffect(() => { + const fetchDeploymentStatus = async () => { + const trainedModelStats = await ml?.mlApi?.trainedModels.getTrainedModelStats(); + if (trainedModelStats) { + const newDeploymentStatus = trainedModelStats?.trained_model_stats.reduce( + (acc, modelStat) => { + if (modelStat.model_id) { + acc[modelStat.model_id] = + modelStat?.deployment_stats?.state === 'started' + ? DeploymentStatusEnum.deployed + : DeploymentStatusEnum.notDeployed; + } + return acc; + }, + {} as Record + ); + setDeploymentStatus(newDeploymentStatus); + } + }; + + fetchDeploymentStatus().catch((error) => { + const errorObj = extractErrorProperties(error); + notifications?.toasts?.addError(errorObj.message ? new Error(error.message) : error, { + title: i18n.TRAINED_MODELS_STAT_GATHER_FAILED, + }); + }); + }, [ml, notifications]); const { paginatedSortedTableData, pagination, sorting } = useTableData( inferenceEndpoints, - queryParams + queryParams, + filterOptions, + searchKey, + deploymentStatus ); + const tableColumns = useTableColumns(); + const handleTableChange = useCallback( ({ page, sort }) => { const newQueryParams = { @@ -46,12 +105,32 @@ export const TabularPage: React.FC = ({ inferenceEndpoints }) ); return ( - + + + + + + + + + + + + + + + + + + ); }; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts index 4afba1dda110a..4a83ac401c89a 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts +++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts @@ -5,8 +5,28 @@ * 2.0. */ +import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; export const INFERENCE_ENDPOINTS_TABLE_PER_PAGE_VALUES = [10, 25, 50, 100]; +export enum ServiceProviderKeys { + azureopenai = 'azureopenai', + azureaistudio = 'azureaistudio', + cohere = 'cohere', + elasticsearch = 'elasticsearch', + elser = 'elser', + googleaistudio = 'googleaistudio', + hugging_face = 'hugging_face', + mistral = 'mistral', + openai = 'openai', +} + +export enum TaskTypes { + completion = 'completion', + rerank = 'rerank', + sparse_embedding = 'sparse_embedding', + text_embedding = 'text_embedding', +} + export enum SortFieldInferenceEndpoint { endpoint = 'endpoint', } @@ -25,7 +45,13 @@ export interface QueryParams extends SortingParams { perPage: number; } -export interface AlInferenceEndpointsTableState { +export interface FilterOptions { + provider: ServiceProviderKeys[]; + type: TaskTypes[]; +} + +export interface AllInferenceEndpointsTableState { + filterOptions: FilterOptions; queryParams: QueryParams; } @@ -34,8 +60,16 @@ export interface EuiBasicTableSortTypes { field: string; } +export enum DeploymentStatusEnum { + deployed = 'deployed', + notDeployed = 'not_deployed', + notDeployable = 'not_deployable', + notApplicable = 'not_applicable', +} + export interface InferenceEndpointUI { - endpoint: string; + deployment: DeploymentStatusEnum; + endpoint: InferenceAPIConfigResponse; provider: string; type: string; } diff --git a/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/add_empty_prompt.tsx b/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/add_empty_prompt.tsx index 69d74724016d6..ee858f7a8b640 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/add_empty_prompt.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/add_empty_prompt.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { EuiButton, - EuiEmptyPrompt, + EuiPageTemplate, EuiFlexGroup, EuiFlexItem, EuiImage, @@ -20,8 +20,7 @@ import * as i18n from '../../../common/translations'; import inferenceEndpoint from '../../assets/images/inference_endpoint.svg'; -import { ElserPrompt } from './elser_prompt'; -import { MultilingualE5Prompt } from './multilingual_e5_prompt'; +import { EndpointPrompt } from './endpoint_prompt'; import './add_empty_prompt.scss'; @@ -31,9 +30,12 @@ interface AddEmptyPromptProps { export const AddEmptyPrompt: React.FC = ({ setIsInferenceFlyoutVisible }) => { return ( - } title={

    {i18n.INFERENCE_ENDPOINT_LABEL}

    } body={ @@ -60,20 +62,41 @@ export const AddEmptyPrompt: React.FC = ({ setIsInferenceFl {i18n.START_WITH_PREPARED_ENDPOINTS_LABEL} - + - + setIsInferenceFlyoutVisible(true)} + > + {i18n.ADD_ENDPOINT_LABEL} + + } + /> - + setIsInferenceFlyoutVisible(true)} + > + {i18n.ADD_ENDPOINT_LABEL} + + } + /> } - color="plain" - hasBorder - icon={} /> ); }; diff --git a/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/elser_prompt.tsx b/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/elser_prompt.tsx deleted file mode 100644 index db38b649fd66e..0000000000000 --- a/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/elser_prompt.tsx +++ /dev/null @@ -1,31 +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 React from 'react'; - -import { EuiButton, EuiCard } from '@elastic/eui'; - -import * as i18n from '../../../common/translations'; - -interface ElserPromptProps { - setIsInferenceFlyoutVisible: (value: boolean) => void; -} -export const ElserPrompt: React.FC = ({ setIsInferenceFlyoutVisible }) => ( - setIsInferenceFlyoutVisible(true)}> - {i18n.ADD_ENDPOINT_LABEL} - - } - /> -); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/multilingual_e5_prompt.tsx b/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/endpoint_prompt.tsx similarity index 51% rename from x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/multilingual_e5_prompt.tsx rename to x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/endpoint_prompt.tsx index 69133909efcb2..b10812ef2e6a3 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/multilingual_e5_prompt.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/empty_prompt/endpoint_prompt.tsx @@ -6,29 +6,28 @@ */ import React from 'react'; +import { EuiCard } from '@elastic/eui'; -import { EuiButton, EuiCard } from '@elastic/eui'; - -import * as i18n from '../../../common/translations'; - -interface MultilingualE5PromptProps { +interface EndpointPromptProps { setIsInferenceFlyoutVisible: (value: boolean) => void; + title: string; + description: string; + footer: React.ReactElement; } -export const MultilingualE5Prompt: React.FC = ({ +export const EndpointPrompt: React.FC = ({ setIsInferenceFlyoutVisible, + title, + description, + footer, }) => ( setIsInferenceFlyoutVisible(true)}> - {i18n.ADD_ENDPOINT_LABEL} - - } + title={title} + titleSize="xs" + description={description} + footer={footer} /> ); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx b/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx index 8b551af28bd4c..6956e470a9b77 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx @@ -5,8 +5,8 @@ * 2.0. */ +import { EuiButton, EuiPageTemplate } from '@elastic/eui'; import React from 'react'; -import { EuiPageTemplate, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui'; import * as i18n from '../../common/translations'; interface InferenceEndpointsHeaderProps { @@ -21,21 +21,18 @@ export const InferenceEndpointsHeader: React.FC = data-test-subj="allInferenceEndpointsPage" pageTitle={i18n.INFERENCE_ENDPOINT_LABEL} description={i18n.MANAGE_INFERENCE_ENDPOINTS_LABEL} + bottomBorder={true} rightSideItems={[ - - - setIsInferenceFlyoutVisible(true)} - > - {i18n.ADD_ENDPOINT_LABEL} - - - , + setIsInferenceFlyoutVisible(true)} + > + {i18n.ADD_ENDPOINT_LABEL} + , ]} /> ); diff --git a/x-pack/plugins/search_inference_endpoints/public/components/inference_flyout_wrapper_component.tsx b/x-pack/plugins/search_inference_endpoints/public/components/inference_flyout_wrapper_component.tsx index bf1343fe3db8b..43dc7d7d1751c 100644 --- a/x-pack/plugins/search_inference_endpoints/public/components/inference_flyout_wrapper_component.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/components/inference_flyout_wrapper_component.tsx @@ -47,9 +47,11 @@ export const InferenceFlyoutWrapperComponent: React.FC { @@ -87,17 +93,26 @@ export const InferenceFlyoutWrapperComponent: React.FC { setIsCreateInferenceApiLoading(true); - try { - await createInferenceEndpointMutation.mutateAsync({ inferenceId, taskType, modelConfig }); - setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); - } catch (error) { - const errorObj = extractErrorProperties(error); - setInferenceAddError(errorObj.message); - } finally { - setIsCreateInferenceApiLoading(false); - } + + createInferenceEndpointMutation + .mutateAsync({ inferenceId, taskType, modelConfig }) + .catch((error) => { + const errorObj = extractErrorProperties(error); + notifications?.toasts?.addError(errorObj.message ? new Error(error.message) : error, { + title: i18n.ENDPOINT_CREATION_FAILED, + }); + }) + .finally(() => { + setIsCreateInferenceApiLoading(false); + }); + setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); }, - [createInferenceEndpointMutation, isInferenceFlyoutVisible, setIsInferenceFlyoutVisible] + [ + createInferenceEndpointMutation, + isInferenceFlyoutVisible, + setIsInferenceFlyoutVisible, + notifications, + ] ); const onFlyoutClose = useCallback(() => { diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/translations.ts b/x-pack/plugins/search_inference_endpoints/public/hooks/translations.ts new file mode 100644 index 0000000000000..ee1dc26a54817 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/hooks/translations.ts @@ -0,0 +1,23 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +export * from '../../common/translations'; + +export const ENDPOINT_DELETION_FAILED = i18n.translate( + 'xpack.searchInferenceEndpoints.deleteEndpoint.endpointDeletionFailed', + { + defaultMessage: 'Endpoint deletion failed', + } +); + +export const DELETE_SUCCESS = i18n.translate( + 'xpack.searchInferenceEndpoints.deleteEndpoint.deleteSuccess', + { + defaultMessage: 'The inference endpoint has been deleted sucessfully.', + } +); diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_all_inference_endpoints_state.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_all_inference_endpoints_state.tsx index 4979cdf7994fc..c2a0486578a9e 100644 --- a/x-pack/plugins/search_inference_endpoints/public/hooks/use_all_inference_endpoints_state.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_all_inference_endpoints_state.tsx @@ -9,7 +9,8 @@ import { useCallback, useState } from 'react'; import type { QueryParams, - AlInferenceEndpointsTableState, + AllInferenceEndpointsTableState, + FilterOptions, } from '../components/all_inference_endpoints/types'; import { DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE } from '../components/all_inference_endpoints/constants'; @@ -17,13 +18,15 @@ import { DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE } from '../components/all_infer interface UseAllInferenceEndpointsStateReturn { queryParams: QueryParams; setQueryParams: (queryParam: Partial) => void; + filterOptions: FilterOptions; + setFilterOptions: (filterOptions: Partial) => void; } export function useAllInferenceEndpointsState(): UseAllInferenceEndpointsStateReturn { - const [tableState, setTableState] = useState( + const [tableState, setTableState] = useState( DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE ); - const setState = useCallback((state: AlInferenceEndpointsTableState) => { + const setState = useCallback((state: AllInferenceEndpointsTableState) => { setTableState(state); }, []); @@ -34,8 +37,19 @@ export function useAllInferenceEndpointsState(): UseAllInferenceEndpointsStateRe }, setQueryParams: (newQueryParams: Partial) => { setState({ + filterOptions: tableState.filterOptions, queryParams: { ...tableState.queryParams, ...newQueryParams }, }); }, + filterOptions: { + ...DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE.filterOptions, + ...tableState.filterOptions, + }, + setFilterOptions: (newFilterOptions: Partial) => { + setState({ + filterOptions: { ...tableState.filterOptions, ...newFilterOptions }, + queryParams: tableState.queryParams, + }); + }, }; } diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.test.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.test.tsx new file mode 100644 index 0000000000000..72e8dfe8418e2 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.test.tsx @@ -0,0 +1,72 @@ +/* + * 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 { renderHook } from '@testing-library/react-hooks'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useKibana } from './use_kibana'; +import { useDeleteEndpoint } from './use_delete_endpoint'; +import * as i18n from './translations'; +import React from 'react'; + +jest.mock('./use_kibana'); + +const mockUseKibana = useKibana as jest.Mock; +const mockDelete = jest.fn(); +const mockAddSuccess = jest.fn(); +const mockAddError = jest.fn(); + +describe('useDeleteEndpoint', () => { + beforeEach(() => { + mockUseKibana.mockReturnValue({ + services: { + http: { + delete: mockDelete, + }, + notifications: { + toasts: { + addSuccess: mockAddSuccess, + addError: mockAddError, + }, + }, + }, + }); + mockDelete.mockResolvedValue({}); + }); + + const wrapper = ({ children }: { children: React.ReactNode }) => { + const queryClient = new QueryClient(); + return {children}; + }; + + it('should call delete endpoint and show success toast on success', async () => { + const { result, waitFor } = renderHook(() => useDeleteEndpoint(), { wrapper }); + + result.current.mutate({ type: 'text_embedding', id: 'in-1' }); + + await waitFor(() => + expect(mockDelete).toHaveBeenCalledWith( + '/internal/inference_endpoint/endpoints/text_embedding/in-1' + ) + ); + expect(mockAddSuccess).toHaveBeenCalledWith({ + title: i18n.DELETE_SUCCESS, + }); + }); + + it('should show error toast on failure', async () => { + const error = new Error('Deletion failed'); + mockDelete.mockRejectedValue(error); + const { result, waitFor } = renderHook(() => useDeleteEndpoint(), { wrapper }); + + result.current.mutate({ type: 'model', id: '123' }); + + await waitFor(() => expect(mockAddError).toHaveBeenCalled()); + expect(mockAddError).toHaveBeenCalledWith(error, { + title: i18n.ENDPOINT_DELETION_FAILED, + }); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.tsx new file mode 100644 index 0000000000000..e5464703dcfe2 --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_delete_endpoint.tsx @@ -0,0 +1,42 @@ +/* + * 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 { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useKibana } from './use_kibana'; +import * as i18n from './translations'; + +import { INFERENCE_ENDPOINTS_QUERY_KEY } from '../../common/constants'; + +interface MutationArgs { + type: string; + id: string; +} + +export const useDeleteEndpoint = () => { + const queryClient = useQueryClient(); + const { services } = useKibana(); + const toasts = services.notifications?.toasts; + + return useMutation( + async ({ type, id }: MutationArgs) => { + await services.http.delete<{}>(`/internal/inference_endpoint/endpoints/${type}/${id}`); + }, + { + onSuccess: () => { + queryClient.invalidateQueries([INFERENCE_ENDPOINTS_QUERY_KEY]); + toasts?.addSuccess({ + title: i18n.DELETE_SUCCESS, + }); + }, + onError: (error: Error) => { + toasts?.addError(new Error(error?.message), { + title: i18n.ENDPOINT_DELETION_FAILED, + }); + }, + } + ); +}; diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx index e0026cbffff98..a8d0326a4c36f 100644 --- a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.test.tsx @@ -36,8 +36,8 @@ const inferenceEndpoints = [ }, { model_id: 'my-elser-model-05', - task_type: 'sparse_embedding', - service: 'elser', + task_type: 'text_embedding', + service: 'elasticsearch', service_settings: { num_allocations: 1, num_threads: 1, @@ -54,9 +54,23 @@ const queryParams = { sortOrder: 'desc', } as QueryParams; +const filterOptions = { + provider: ['elser', 'elasticsearch'], + type: ['sparse_embedding', 'text_embedding'], +} as any; + +const deploymentStatus = { + '.elser_model_2': 'deployed', + lang_ident_model_1: 'not_deployed', +} as any; + +const searchKey = 'my'; + describe('useTableData', () => { it('should return correct pagination', () => { - const { result } = renderHook(() => useTableData(inferenceEndpoints, queryParams)); + const { result } = renderHook(() => + useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus) + ); expect(result.current.pagination).toEqual({ pageIndex: 0, @@ -67,7 +81,9 @@ describe('useTableData', () => { }); it('should return correct sorting', () => { - const { result } = renderHook(() => useTableData(inferenceEndpoints, queryParams)); + const { result } = renderHook(() => + useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus) + ); expect(result.current.sorting).toEqual({ sort: { @@ -78,15 +94,54 @@ describe('useTableData', () => { }); it('should return correctly sorted data', () => { - const { result } = renderHook(() => useTableData(inferenceEndpoints, queryParams)); + const { result } = renderHook(() => + useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus) + ); const expectedSortedData = [...inferenceEndpoints].sort((a, b) => b.model_id.localeCompare(a.model_id) ); - const sortedEndpoints = result.current.sortedTableData.map((item) => item.endpoint); + const sortedEndpoints = result.current.sortedTableData.map((item) => item.endpoint.model_id); const expectedModelIds = expectedSortedData.map((item) => item.model_id); expect(sortedEndpoints).toEqual(expectedModelIds); }); + + it('should filter data based on provider and type from filterOptions', () => { + const filterOptions2 = { + provider: ['elser'], + type: ['text_embedding'], + } as any; + const { result } = renderHook(() => + useTableData(inferenceEndpoints, queryParams, filterOptions2, searchKey, deploymentStatus) + ); + + const filteredData = result.current.sortedTableData; + expect( + filteredData.every( + (endpoint) => + filterOptions.provider.includes(endpoint.provider) && + filterOptions.type.includes(endpoint.type) + ) + ).toBeTruthy(); + }); + + it('should filter data based on searchKey', () => { + const searchKey2 = 'model-05'; + const { result } = renderHook(() => + useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey2, deploymentStatus) + ); + const filteredData = result.current.sortedTableData; + expect(filteredData.every((item) => item.endpoint.model_id.includes(searchKey))).toBeTruthy(); + }); + + it('should update deployment status based on deploymentStatus object', () => { + const { result } = renderHook(() => + useTableData(inferenceEndpoints, queryParams, filterOptions, searchKey, deploymentStatus) + ); + + const updatedData = result.current.sortedTableData; + expect(updatedData[0].deployment).toEqual('deployed'); + }); }); diff --git a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx index 304c469e06093..54d865aad5190 100644 --- a/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx +++ b/x-pack/plugins/search_inference_endpoints/public/hooks/use_table_data.tsx @@ -11,11 +11,15 @@ import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import { useMemo } from 'react'; import { DEFAULT_TABLE_LIMIT } from '../components/all_inference_endpoints/constants'; import { - InferenceEndpointUI, + FilterOptions, INFERENCE_ENDPOINTS_TABLE_PER_PAGE_VALUES, + InferenceEndpointUI, QueryParams, SortOrder, + ServiceProviderKeys, + TaskTypes, } from '../components/all_inference_endpoints/types'; +import { DeploymentStatusEnum } from '../components/all_inference_endpoints/types'; interface UseTableDataReturn { tableData: InferenceEndpointUI[]; @@ -27,15 +31,50 @@ interface UseTableDataReturn { export const useTableData = ( inferenceEndpoints: InferenceAPIConfigResponse[], - queryParams: QueryParams + queryParams: QueryParams, + filterOptions: FilterOptions, + searchKey: string, + deploymentStatus: Record ): UseTableDataReturn => { const tableData: InferenceEndpointUI[] = useMemo(() => { - return inferenceEndpoints.map((endpoint) => ({ - endpoint: endpoint.model_id, - provider: endpoint.service, - type: endpoint.task_type, - })); - }, [inferenceEndpoints]); + let filteredEndpoints = inferenceEndpoints; + + if (filterOptions.provider.length > 0) { + filteredEndpoints = filteredEndpoints.filter((endpoint) => + filterOptions.provider.includes(ServiceProviderKeys[endpoint.service]) + ); + } + + if (filterOptions.type.length > 0) { + filteredEndpoints = filteredEndpoints.filter((endpoint) => + filterOptions.type.includes(TaskTypes[endpoint.task_type]) + ); + } + + return filteredEndpoints + .filter((endpoint) => endpoint.model_id.includes(searchKey)) + .map((endpoint) => { + const isElasticService = + endpoint.service === ServiceProviderKeys.elasticsearch || + endpoint.service === ServiceProviderKeys.elser; + + let deploymentStatusValue = DeploymentStatusEnum.notApplicable; + if (isElasticService) { + const modelId = endpoint.service_settings?.model_id; + deploymentStatusValue = + modelId && deploymentStatus[modelId] !== undefined + ? deploymentStatus[modelId] + : DeploymentStatusEnum.notDeployable; + } + + return { + deployment: deploymentStatusValue, + endpoint, + provider: endpoint.service, + type: endpoint.task_type, + }; + }); + }, [inferenceEndpoints, searchKey, filterOptions, deploymentStatus]); const sortedTableData: InferenceEndpointUI[] = useMemo(() => { return [...tableData].sort((a, b) => { @@ -43,9 +82,9 @@ export const useTableData = ( const bValue = b[queryParams.sortField]; if (queryParams.sortOrder === SortOrder.asc) { - return aValue.localeCompare(bValue); + return aValue.model_id.localeCompare(bValue.model_id); } else { - return bValue.localeCompare(aValue); + return bValue.model_id.localeCompare(aValue.model_id); } }); }, [tableData, queryParams]); diff --git a/x-pack/plugins/search_inference_endpoints/server/lib/delete_inference_endpoint.test.ts b/x-pack/plugins/search_inference_endpoints/server/lib/delete_inference_endpoint.test.ts new file mode 100644 index 0000000000000..f6c9a5625230d --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/server/lib/delete_inference_endpoint.test.ts @@ -0,0 +1,32 @@ +/* + * 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 { deleteInferenceEndpoint } from './delete_inference_endpoint'; + +describe('deleteInferenceEndpoint', () => { + let mockClient: any; + + beforeEach(() => { + mockClient = { + transport: { + request: jest.fn(), + }, + }; + }); + + it('should call the Elasticsearch client with the correct DELETE request', async () => { + const type = 'model'; + const id = 'model-id-123'; + + await deleteInferenceEndpoint(mockClient, type, id); + + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'DELETE', + path: `/_inference/${type}/${id}`, + }); + }); +}); diff --git a/x-pack/plugins/search_inference_endpoints/server/lib/delete_inference_endpoint.ts b/x-pack/plugins/search_inference_endpoints/server/lib/delete_inference_endpoint.ts new file mode 100644 index 0000000000000..c294820e4943e --- /dev/null +++ b/x-pack/plugins/search_inference_endpoints/server/lib/delete_inference_endpoint.ts @@ -0,0 +1,19 @@ +/* + * 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 { ElasticsearchClient } from '@kbn/core/server'; + +export const deleteInferenceEndpoint = async ( + client: ElasticsearchClient, + type: string, + id: string +): Promise => { + return await client.transport.request({ + method: 'DELETE', + path: `/_inference/${type}/${id}`, + }); +}; diff --git a/x-pack/plugins/search_inference_endpoints/server/routes.ts b/x-pack/plugins/search_inference_endpoints/server/routes.ts index d5f010b902c52..7456888aabb19 100644 --- a/x-pack/plugins/search_inference_endpoints/server/routes.ts +++ b/x-pack/plugins/search_inference_endpoints/server/routes.ts @@ -6,10 +6,12 @@ */ import { IRouter } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; import type { Logger } from '@kbn/logging'; import { fetchInferenceEndpoints } from './lib/fetch_inference_endpoints'; import { APIRoutes } from './types'; import { errorHandler } from './utils/error_handler'; +import { deleteInferenceEndpoint } from './lib/delete_inference_endpoint'; export function defineRoutes({ logger, router }: { logger: Logger; router: IRouter }) { router.get( @@ -32,4 +34,27 @@ export function defineRoutes({ logger, router }: { logger: Logger; router: IRout }); }) ); + + router.delete( + { + path: APIRoutes.DELETE_INFERENCE_ENDPOINT, + validate: { + params: schema.object({ + type: schema.string(), + id: schema.string(), + }), + }, + }, + errorHandler(logger)(async (context, request, response) => { + const { + client: { asCurrentUser }, + } = (await context.core).elasticsearch; + + const { type, id } = request.params; + + await deleteInferenceEndpoint(asCurrentUser, type, id); + + return response.ok(); + }) + ); } diff --git a/x-pack/plugins/search_notebooks/kibana.jsonc b/x-pack/plugins/search_notebooks/kibana.jsonc index d702cfb020275..e9432a5559ce9 100644 --- a/x-pack/plugins/search_notebooks/kibana.jsonc +++ b/x-pack/plugins/search_notebooks/kibana.jsonc @@ -15,7 +15,9 @@ "requiredPlugins": [ "console" ], - "optionalPlugins": [], + "optionalPlugins": [ + "usageCollection" + ], "requiredBundles": [ "kibanaReact" ] diff --git a/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx b/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx index a20c3b9ddaf2d..b6c837f70ba37 100644 --- a/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx +++ b/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React from 'react'; import { CoreStart } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; @@ -11,24 +12,32 @@ import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { SearchNotebooks } from './search_notebooks'; -import { NotebookListValue } from '../types'; +import { AppMetricsTracker, NotebookListValue } from '../types'; export interface SearchNotebooksViewProps { core: CoreStart; queryClient: QueryClient; + usageTracker: AppMetricsTracker; getNotebookList: () => NotebookListValue; } export const SearchNotebooksView = ({ core, queryClient, + usageTracker, getNotebookList, -}: SearchNotebooksViewProps) => ( - - - - - - - -); +}: SearchNotebooksViewProps) => { + React.useEffect(() => { + usageTracker.count('opened_notebooks_view'); + }, [usageTracker]); + + return ( + + + + + + + + ); +}; diff --git a/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx b/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx index 9c78344c15bab..e075c52c53e80 100644 --- a/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx +++ b/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx @@ -4,48 +4,31 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; -import { EuiPanel, EuiEmptyPrompt, EuiCodeBlock } from '@elastic/eui'; +import React, { useEffect } from 'react'; +import { EuiPanel } from '@elastic/eui'; import { NotebookRenderer } from '@kbn/ipynb'; -import { FormattedMessage } from '@kbn/i18n-react'; import { useNotebook } from '../hooks/use_notebook'; +import { useUsageTracker } from '../hooks/use_usage_tracker'; import { LoadingPanel } from './loading_panel'; +import { SearchNotebookError } from './search_notebook_error'; export interface SearchNotebookProps { notebookId: string; } export const SearchNotebook = ({ notebookId }: SearchNotebookProps) => { + const usageTracker = useUsageTracker(); const { data, isLoading, error } = useNotebook(notebookId); + useEffect(() => { + usageTracker.count(['view-notebook', `nb-${notebookId}`]); + }, [usageTracker, notebookId]); + if (isLoading) { return ; } if (!data || error) { return ( - - - - } - titleSize="l" - body={ - <> -

    - -

    - {JSON.stringify(error)} - - } - /> + ); } return ( diff --git a/x-pack/plugins/search_notebooks/public/components/search_notebook_error.tsx b/x-pack/plugins/search_notebooks/public/components/search_notebook_error.tsx new file mode 100644 index 0000000000000..87c0bf74fe941 --- /dev/null +++ b/x-pack/plugins/search_notebooks/public/components/search_notebook_error.tsx @@ -0,0 +1,67 @@ +/* + * 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 React from 'react'; +import { EuiEmptyPrompt, EuiCodeBlock } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import type { AppMetricsTracker } from '../types'; +import { getErrorMessage } from '../utils/get_error_message'; + +export interface SearchNotebookErrorProps { + error: unknown; + notebookId: string; + usageTracker: AppMetricsTracker; +} + +export const SearchNotebookError = ({ + error, + notebookId, + usageTracker, +}: SearchNotebookErrorProps) => { + React.useEffect(() => { + usageTracker.count(['notebookViewError', `error-${notebookId}`]); + }, [usageTracker, notebookId]); + + return ( + + + + } + titleSize="l" + body={ + <> +

    + +

    + {error !== undefined && typeof error === 'object' ? ( + {JSON.stringify(error)} + ) : ( +

    + {getErrorMessage( + error, + i18n.translate('xpack.searchNotebooks.notebook.fetchError.unknownError', { + defaultMessage: 'Unknown error fetching notebook', + }) + )} +

    + )} + + } + /> + ); +}; diff --git a/x-pack/plugins/search_notebooks/public/console_view.tsx b/x-pack/plugins/search_notebooks/public/console_view.tsx index 99575c8e93948..0f64ed989f5e2 100644 --- a/x-pack/plugins/search_notebooks/public/console_view.tsx +++ b/x-pack/plugins/search_notebooks/public/console_view.tsx @@ -14,7 +14,7 @@ import type { import { dynamic } from '@kbn/shared-ux-utility'; import { QueryClient } from '@tanstack/react-query'; -import { NotebookListValue } from './types'; +import { NotebookListValue, AppMetricsTracker } from './types'; const SearchNotebooksButton = dynamic(async () => ({ default: (await import('./components/notebooks_button')).SearchNotebooksButton, @@ -26,6 +26,7 @@ const SearchNotebooksView = dynamic(async () => ({ export const notebooksConsoleView = ( core: CoreStart, queryClient: QueryClient, + usageTracker: AppMetricsTracker, clearNotebookList: () => void, getNotebookListValue: () => NotebookListValue ): EmbeddedConsoleView => { @@ -37,6 +38,7 @@ export const notebooksConsoleView = ( ), diff --git a/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts b/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts index b490e61f41490..fc2a5511b9336 100644 --- a/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts +++ b/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts @@ -9,13 +9,14 @@ import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import { useKibana as useKibanaBase } from '@kbn/kibana-react-plugin/public'; -import { NotebookListValue } from '../types'; +import { NotebookListValue, AppMetricsTracker } from '../types'; export interface SearchNotebooksContext { console: ConsolePluginStart; notebooks: { getNotebookList: () => NotebookListValue; }; + usageTracker: AppMetricsTracker; } type ServerlessSearchKibanaContext = CoreStart & SearchNotebooksContext; diff --git a/x-pack/plugins/search_notebooks/public/hooks/use_usage_tracker.ts b/x-pack/plugins/search_notebooks/public/hooks/use_usage_tracker.ts new file mode 100644 index 0000000000000..52e583d6af358 --- /dev/null +++ b/x-pack/plugins/search_notebooks/public/hooks/use_usage_tracker.ts @@ -0,0 +1,15 @@ +/* + * 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 { useKibanaServices } from './use_kibana'; +import { AppMetricsTracker } from '../types'; + +export const useUsageTracker = (): AppMetricsTracker => { + const { usageTracker } = useKibanaServices(); + + return usageTracker; +}; diff --git a/x-pack/plugins/search_notebooks/public/plugin.ts b/x-pack/plugins/search_notebooks/public/plugin.ts index 3b71193ed3add..e4e1c2b4f0d00 100644 --- a/x-pack/plugins/search_notebooks/public/plugin.ts +++ b/x-pack/plugins/search_notebooks/public/plugin.ts @@ -14,14 +14,17 @@ import { SearchNotebooksPluginStart, SearchNotebooksPluginStartDependencies, NotebookListValue, + AppMetricsTracker, } from './types'; import { getErrorCode, getErrorMessage, isKibanaServerError } from './utils/get_error_message'; +import { createUsageTracker } from './utils/usage_tracker'; export class SearchNotebooksPlugin implements Plugin { private notebooksList: NotebookListValue = null; private queryClient: QueryClient | undefined; + private usageTracker: AppMetricsTracker | undefined; public setup(core: CoreSetup): SearchNotebooksPluginSetup { this.queryClient = new QueryClient({ @@ -56,11 +59,13 @@ export class SearchNotebooksPlugin core: CoreStart, deps: SearchNotebooksPluginStartDependencies ): SearchNotebooksPluginStart { + this.usageTracker = createUsageTracker(deps.usageCollection); if (deps.console?.registerEmbeddedConsoleAlternateView) { deps.console.registerEmbeddedConsoleAlternateView( notebooksConsoleView( core, this.queryClient!, + this.usageTracker, this.clearNotebookList.bind(this), this.getNotebookList.bind(this) ) diff --git a/x-pack/plugins/search_notebooks/public/types.ts b/x-pack/plugins/search_notebooks/public/types.ts index 150d08a5d463f..63955ceb85029 100644 --- a/x-pack/plugins/search_notebooks/public/types.ts +++ b/x-pack/plugins/search_notebooks/public/types.ts @@ -6,6 +6,7 @@ */ import type { ConsolePluginStart } from '@kbn/console-plugin/public'; +import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SearchNotebooksPluginSetup {} @@ -16,6 +17,13 @@ export interface SearchNotebooksPluginStart { export interface SearchNotebooksPluginStartDependencies { console: ConsolePluginStart; + usageCollection?: UsageCollectionStart; } export type NotebookListValue = string | null; + +export interface AppMetricsTracker { + click: (eventName: string | string[]) => void; + count: (eventName: string | string[]) => void; + load: (eventName: string | string[]) => void; +} diff --git a/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts b/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts index 0a73af9e544ce..4625b2cf5240c 100644 --- a/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts +++ b/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts @@ -7,7 +7,7 @@ import { KibanaServerError } from '@kbn/kibana-utils-plugin/common'; -export function getErrorMessage(error: unknown): string { +export function getErrorMessage(error: unknown, defaultMessage?: string): string { if (typeof error === 'string') { return error; } @@ -19,7 +19,7 @@ export function getErrorMessage(error: unknown): string { return (error as { name: string }).name; } - return ''; + return defaultMessage ?? ''; } export function getErrorCode(error: unknown): number | undefined { diff --git a/x-pack/plugins/search_notebooks/public/utils/usage_tracker.ts b/x-pack/plugins/search_notebooks/public/utils/usage_tracker.ts new file mode 100644 index 0000000000000..9a85626c73608 --- /dev/null +++ b/x-pack/plugins/search_notebooks/public/utils/usage_tracker.ts @@ -0,0 +1,33 @@ +/* + * 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 { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; +import type { + UsageCollectionSetup, + UsageCollectionStart, +} from '@kbn/usage-collection-plugin/public'; +import { AppMetricsTracker } from '../types'; + +const APP_TRACKER_NAME = 'searchNotebooks'; + +export function createUsageTracker( + usageCollection?: UsageCollectionSetup | UsageCollectionStart +): AppMetricsTracker { + const track = (type: UiCounterMetricType, name: string | string[]) => + usageCollection?.reportUiCounter(APP_TRACKER_NAME, type, name); + return { + click: (eventName: string | string[]) => { + track(METRIC_TYPE.CLICK, eventName); + }, + count: (eventName: string | string[]) => { + track(METRIC_TYPE.COUNT, eventName); + }, + load: (eventName: string | string[]) => { + track(METRIC_TYPE.LOADED, eventName); + }, + }; +} diff --git a/x-pack/plugins/search_notebooks/tsconfig.json b/x-pack/plugins/search_notebooks/tsconfig.json index b50ec60a5bed5..5f9d5efaa0308 100644 --- a/x-pack/plugins/search_notebooks/tsconfig.json +++ b/x-pack/plugins/search_notebooks/tsconfig.json @@ -15,6 +15,7 @@ "target/**/*" ], "kbn_references": [ + "@kbn/analytics", "@kbn/config-schema", "@kbn/core", "@kbn/i18n", @@ -26,5 +27,6 @@ "@kbn/shared-ux-utility", "@kbn/kibana-utils-plugin", "@kbn/ipynb", + "@kbn/usage-collection-plugin", ] } diff --git a/x-pack/plugins/search_playground/__mocks__/search_playground_mock.ts b/x-pack/plugins/search_playground/__mocks__/search_playground_mock.ts index d1bde7eaf53a5..1a73128730324 100644 --- a/x-pack/plugins/search_playground/__mocks__/search_playground_mock.ts +++ b/x-pack/plugins/search_playground/__mocks__/search_playground_mock.ts @@ -12,7 +12,6 @@ export type Start = jest.Mocked; const createStartMock = (): Start => { const startContract: Start = { PlaygroundProvider: jest.fn(), - PlaygroundToolbar: jest.fn(), Playground: jest.fn(), PlaygroundHeaderDocs: jest.fn(), }; diff --git a/x-pack/plugins/search_playground/public/analytics/constants.ts b/x-pack/plugins/search_playground/public/analytics/constants.ts index 30347e67bac82..055b5b74b201b 100644 --- a/x-pack/plugins/search_playground/public/analytics/constants.ts +++ b/x-pack/plugins/search_playground/public/analytics/constants.ts @@ -13,24 +13,21 @@ export enum AnalyticsEvents { chatRegenerateMessages = 'chat_regenerate_messages', citationDetailsExpanded = 'citation_details_expanded', citationDetailsCollapsed = 'citation_details_collapsed', - editContextFlyoutOpened = 'edit_context_flyout_opened', editContextFieldToggled = 'edit_context_field_toggled', editContextDocSizeChanged = 'edit_context_doc_size_changed', - editContextSaved = 'edit_context_saved', genAiConnectorAdded = 'gen_ai_connector_added', genAiConnectorCreated = 'gen_ai_connector_created', genAiConnectorExists = 'gen_ai_connector_exists', genAiConnectorSetup = 'gen_ai_connector_setup', includeCitations = 'include_citations', instructionsFieldChanged = 'instructions_field_changed', + queryFieldsUpdated = 'view_query_fields_updated', + queryModeLoaded = 'query_mode_loaded', modelSelected = 'model_selected', retrievalDocsFlyoutOpened = 'retrieval_docs_flyout_opened', sourceFieldsLoaded = 'source_fields_loaded', sourceIndexUpdated = 'source_index_updated', - startNewChatPageLoaded = 'start_new_chat_page_loaded', - viewQueryFlyoutOpened = 'view_query_flyout_opened', - viewQueryFieldsUpdated = 'view_query_fields_updated', - viewQuerySaved = 'view_query_saved', + setupChatPageLoaded = 'start_new_chat_page_loaded', viewCodeFlyoutOpened = 'view_code_flyout_opened', viewCodeLanguageChange = 'view_code_language_change', } diff --git a/x-pack/plugins/search_playground/public/chat_playground_overview.tsx b/x-pack/plugins/search_playground/public/chat_playground_overview.tsx index fc350356e873a..b5d753e6044f2 100644 --- a/x-pack/plugins/search_playground/public/chat_playground_overview.tsx +++ b/x-pack/plugins/search_playground/public/chat_playground_overview.tsx @@ -5,15 +5,13 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; -import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPageTemplate, EuiTitle } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiPageTemplate } from '@elastic/eui'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { queryClient } from './utils/query_client'; import { PlaygroundProvider } from './providers/playground_provider'; import { App } from './components/app'; -import { PlaygroundToolbar } from './embeddable'; -import { PlaygroundHeaderDocs } from './components/playground_header_docs'; import { useKibana } from './hooks/use_kibana'; export const ChatPlaygroundOverview: React.FC = () => { @@ -27,46 +25,19 @@ export const ChatPlaygroundOverview: React.FC = () => { ); return ( - - - .euiFlexGroup': { flexWrap: 'wrap' } }} - pageTitle={ - - - -

    - -

    -
    -
    - - - -
    - } - data-test-subj="chat-playground-home-page" - rightSideItems={[, ]} - /> - - {embeddableConsole} -
    -
    + + + + + {embeddableConsole} + + + ); }; diff --git a/x-pack/plugins/search_playground/public/components/app.tsx b/x-pack/plugins/search_playground/public/components/app.tsx index 854be8d3ac760..b779897dc4afa 100644 --- a/x-pack/plugins/search_playground/public/components/app.tsx +++ b/x-pack/plugins/search_playground/public/components/app.tsx @@ -5,28 +5,61 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; -import { StartNewChat } from './start_new_chat'; +import { useFormContext } from 'react-hook-form'; +import { QueryMode } from './query_mode/query_mode'; +import { SetupPage } from './setup_page/setup_page'; +import { Header } from './header'; +import { useLoadConnectors } from '../hooks/use_load_connectors'; +import { ChatForm, ChatFormFields } from '../types'; import { Chat } from './chat'; -export const App: React.FC = () => { - const [showStartPage, setShowStartPage] = useState(true); +export interface AppProps { + showDocs?: boolean; +} + +export enum ViewMode { + chat = 'chat', + query = 'query', +} + +export const App: React.FC = ({ showDocs = false }) => { + const [showSetupPage, setShowSetupPage] = useState(true); + const [selectedMode, setSelectedMode] = useState(ViewMode.chat); + const { watch } = useFormContext(); + const { data: connectors } = useLoadConnectors(); + const hasSelectedIndices = watch(ChatFormFields.indices).length; + const handleModeChange = (id: string) => setSelectedMode(id as ViewMode); + + useEffect(() => { + if (showSetupPage && connectors?.length && hasSelectedIndices) { + setShowSetupPage(false); + } + }, [connectors, hasSelectedIndices, showSetupPage]); return ( - - {showStartPage ? setShowStartPage(false)} /> : } - + <> +
    + + {showSetupPage ? : selectedMode === ViewMode.chat ? : } + + ); }; diff --git a/x-pack/plugins/search_playground/public/components/chat.tsx b/x-pack/plugins/search_playground/public/components/chat.tsx index de0a296e162b0..cc4c0b1ccdff2 100644 --- a/x-pack/plugins/search_playground/public/components/chat.tsx +++ b/x-pack/plugins/search_playground/public/components/chat.tsx @@ -13,6 +13,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiForm, + EuiHideFor, EuiHorizontalRule, EuiSpacer, useEuiTheme, @@ -50,14 +51,12 @@ export const Chat = () => { const { euiTheme } = useEuiTheme(); const { control, - watch, formState: { isValid, isSubmitting }, resetField, handleSubmit, getValues, } = useFormContext(); const { messages, append, stop: stopRequest, setMessages, reload, error } = useChat(); - const selectedIndicesCount = watch(ChatFormFields.indices, []).length; const messagesRef = useAutoBottomScroll(); const [isRegenerating, setIsRegenerating] = useState(false); const usageTracker = useUsageTracker(); @@ -123,7 +122,6 @@ export const Chat = () => { { > - + - + { iconType="refresh" disabled={isToolBarActionsDisabled} onClick={handleClearChat} + size="xs" data-test-subj="clearChatActionButton" > { - + { - - - + + + + + ); diff --git a/x-pack/plugins/search_playground/public/components/chat_sidebar.tsx b/x-pack/plugins/search_playground/public/components/chat_sidebar.tsx index 2172ee8831ead..5686a2d14b642 100644 --- a/x-pack/plugins/search_playground/public/components/chat_sidebar.tsx +++ b/x-pack/plugins/search_playground/public/components/chat_sidebar.tsx @@ -6,84 +6,91 @@ */ import { - EuiAccordion, + EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, - EuiSpacer, - EuiText, + EuiLink, EuiTitle, useEuiTheme, - useGeneratedHtmlId, } from '@elastic/eui'; -import React, { useState } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { SourcesPanelSidebar } from './sources_panel/sources_panel_sidebar'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useWatch } from 'react-hook-form'; +import { docLinks } from '../../common/doc_links'; +import { EditContextPanel } from './edit_context/edit_context_panel'; +import { ChatForm, ChatFormFields } from '../types'; +import { useManagementLink } from '../hooks/use_management_link'; import { SummarizationPanel } from './summarization_panel/summarization_panel'; -interface ChatSidebarProps { - selectedIndicesCount: number; -} - -export const ChatSidebar: React.FC = ({ selectedIndicesCount }) => { +export const ChatSidebar: React.FC = () => { const { euiTheme } = useEuiTheme(); - const accordions = [ + const selectedModel = useWatch({ + name: ChatFormFields.summarizationModel, + }); + const managementLink = useManagementLink(selectedModel?.connectorId); + const panels = [ { - id: useGeneratedHtmlId({ prefix: 'summarizationAccordion' }), title: i18n.translate('xpack.searchPlayground.sidebar.summarizationTitle', { defaultMessage: 'Model settings', }), children: , - dataTestId: 'summarizationAccordion', + extraAction: ( + + + + ), }, { - id: useGeneratedHtmlId({ prefix: 'sourcesAccordion' }), - title: i18n.translate('xpack.searchPlayground.sidebar.sourceTitle', { - defaultMessage: 'Indices', + title: i18n.translate('xpack.searchPlayground.sidebar.contextTitle', { + defaultMessage: 'Context', }), - extraAction: !!selectedIndicesCount && ( - -

    - {i18n.translate('xpack.searchPlayground.sidebar.sourceIndicesCount', { - defaultMessage: '{count, number} {count, plural, one {Index} other {Indices}}', - values: { count: Number(selectedIndicesCount) }, - })} -

    -
    + extraAction: ( + + + ), - children: , - dataTestId: 'sourcesAccordion', + children: , }, ]; - const [openAccordionId, setOpenAccordionId] = useState(accordions[0].id); return ( - {accordions.map(({ id, title, extraAction, children, dataTestId }, index) => ( - - -
    {title}
    - - } - extraAction={extraAction} - buttonProps={{ paddingSize: 'l' }} - forceState={openAccordionId === id ? 'open' : 'closed'} - onToggle={() => setOpenAccordionId(openAccordionId === id ? '' : id)} - data-test-subj={dataTestId} - > - {children} - -
    + {panels?.map(({ title, children, extraAction }) => ( + + + + + +

    {title}

    +
    + {extraAction && {extraAction}} +
    +
    + {children} +
    ))}
    diff --git a/x-pack/plugins/search_playground/public/components/data_action_button.tsx b/x-pack/plugins/search_playground/public/components/data_action_button.tsx new file mode 100644 index 0000000000000..abc2a0c265cf9 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/data_action_button.tsx @@ -0,0 +1,37 @@ +/* + * 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 React, { useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiButton } from '@elastic/eui'; +import { useWatch } from 'react-hook-form'; +import { ChatForm, ChatFormFields } from '../types'; +import { SelectIndicesFlyout } from './select_indices_flyout'; + +export const DataActionButton: React.FC = () => { + const selectedIndices = useWatch({ + name: ChatFormFields.indices, + }); + const [showFlyout, setShowFlyout] = useState(false); + const handleFlyoutClose = () => setShowFlyout(false); + const handleShowFlyout = () => setShowFlyout(true); + + return ( + <> + {showFlyout && } + + + + + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_action.tsx b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_action.tsx deleted file mode 100644 index 6e9a638248660..0000000000000 --- a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_action.tsx +++ /dev/null @@ -1,37 +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 React, { useState } from 'react'; -import { EuiButtonEmpty } from '@elastic/eui'; -import { useFormContext } from 'react-hook-form'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { ChatForm } from '../../types'; -import { EditContextFlyout } from './edit_context_flyout'; - -export const EditContextAction: React.FC = () => { - const { watch } = useFormContext(); - const [showFlyout, setShowFlyout] = useState(false); - const selectedIndices: string[] = watch('indices'); - - const closeFlyout = () => setShowFlyout(false); - - return ( - <> - {showFlyout && } - setShowFlyout(true)} - disabled={selectedIndices?.length === 0} - data-test-subj="editContextActionButton" - > - - - - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.tsx b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.tsx deleted file mode 100644 index cb6968dcce6e4..0000000000000 --- a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.tsx +++ /dev/null @@ -1,211 +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 React, { useEffect, useState } from 'react'; -import { - EuiFlyout, - EuiFlyoutHeader, - EuiFlyoutBody, - EuiTitle, - EuiFlexItem, - EuiFlexGroup, - EuiButtonEmpty, - EuiButton, - EuiFlyoutFooter, - EuiLink, - EuiSpacer, - EuiText, - EuiSelect, - EuiSuperSelect, - EuiFormRow, -} from '@elastic/eui'; -import { useController, useFormContext } from 'react-hook-form'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { docLinks } from '../../../common/doc_links'; -import { useUsageTracker } from '../../hooks/use_usage_tracker'; -import { ChatForm, ChatFormFields } from '../../types'; -import { useIndicesFields } from '../../hooks/use_indices_fields'; -import { getDefaultSourceFields } from '../../utils/create_query'; -import { AnalyticsEvents } from '../../analytics/constants'; - -interface EditContextFlyoutProps { - onClose: () => void; -} - -export const EditContextFlyout: React.FC = ({ onClose }) => { - const usageTracker = useUsageTracker(); - const { getValues } = useFormContext(); - const selectedIndices: string[] = getValues(ChatFormFields.indices); - const { fields } = useIndicesFields(selectedIndices); - const defaultFields = getDefaultSourceFields(fields); - - const { - field: { onChange: onChangeSize, value: docSizeInitialValue }, - } = useController({ - name: ChatFormFields.docSize, - }); - - const [docSize, setDocSize] = useState(docSizeInitialValue); - - const { - field: { onChange: onChangeSourceFields, value: sourceFields }, - } = useController({ - name: ChatFormFields.sourceFields, - defaultValue: defaultFields, - }); - - const [tempSourceFields, setTempSourceFields] = useState(sourceFields); - - const updateSourceField = (index: string, field: string) => { - setTempSourceFields({ - ...tempSourceFields, - [index]: [field], - }); - usageTracker?.click(AnalyticsEvents.editContextFieldToggled); - }; - - const saveSourceFields = () => { - usageTracker?.click(AnalyticsEvents.editContextSaved); - onChangeSourceFields(tempSourceFields); - onChangeSize(docSize); - onClose(); - }; - - const handleDocSizeChange = (e: React.ChangeEvent) => { - usageTracker?.click(AnalyticsEvents.editContextDocSizeChanged); - setDocSize(Number(e.target.value)); - }; - - useEffect(() => { - usageTracker?.load(AnalyticsEvents.editContextFlyoutOpened); - }, [usageTracker]); - - return ( - - - -

    - -

    -
    - - -

    - - - - -

    -
    -
    - - - - - - - - - - - - - -
    - -
    -
    -
    - {Object.entries(fields).map(([index, group]) => ( - - - ({ - value: field, - inputDisplay: field, - 'data-test-subj': 'contextField', - }))} - valueOfSelected={tempSourceFields[index]?.[0]} - onChange={(value) => updateSourceField(index, value)} - /> - - - ))} -
    -
    -
    -
    -
    -
    - - - - - - - - - - - - - - -
    - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.test.tsx b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_panel.test.tsx similarity index 67% rename from x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.test.tsx rename to x-pack/plugins/search_playground/public/components/edit_context/edit_context_panel.test.tsx index de82892b167be..ac86efd665b27 100644 --- a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_flyout.test.tsx +++ b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_panel.test.tsx @@ -7,12 +7,13 @@ import React from 'react'; import { render, fireEvent, screen } from '@testing-library/react'; -import { EditContextFlyout } from './edit_context_flyout'; +import { EditContextPanel } from './edit_context_panel'; import { FormProvider, useForm } from 'react-hook-form'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ChatFormFields } from '../../types'; -jest.mock('../../hooks/use_indices_fields', () => ({ - useIndicesFields: () => ({ +jest.mock('../../hooks/use_source_indices_field', () => ({ + useSourceIndicesFields: () => ({ fields: { index1: { elser_query_fields: [], @@ -43,9 +44,9 @@ jest.mock('../../hooks/use_usage_tracker', () => ({ const MockFormProvider = ({ children }: { children: React.ReactElement }) => { const methods = useForm({ values: { - indices: ['index1'], - docSize: 1, - sourceFields: { + [ChatFormFields.indices]: ['index1'], + [ChatFormFields.docSize]: 1, + [ChatFormFields.sourceFields]: { index1: ['context_field1'], index2: ['context_field2'], }, @@ -55,27 +56,20 @@ const MockFormProvider = ({ children }: { children: React.ReactElement }) => { }; describe('EditContextFlyout component tests', () => { - const onCloseMock = jest.fn(); - beforeEach(() => { render( - + ); }); - it('calls onClose when the close button is clicked', () => { - fireEvent.click(screen.getByRole('button', { name: 'Close' })); - expect(onCloseMock).toHaveBeenCalledTimes(1); - }); - it('should see the context fields', async () => { - expect(screen.getByTestId('contextFieldsSelectable_index1')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('contextFieldsSelectable_index1')); - expect(screen.getByRole('option', { name: 'context_field1' })).toBeInTheDocument(); - expect(screen.getByRole('option', { name: 'context_field2' })).toBeInTheDocument(); + expect(screen.getByTestId('contextFieldsSelectable-0')).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('contextFieldsSelectable-0')); + const fields = await screen.findAllByTestId('contextField'); + expect(fields.length).toBe(2); }); }); diff --git a/x-pack/plugins/search_playground/public/components/edit_context/edit_context_panel.tsx b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_panel.tsx new file mode 100644 index 0000000000000..1283730efa9cc --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/edit_context/edit_context_panel.tsx @@ -0,0 +1,137 @@ +/* + * 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 { + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiPanel, + EuiSelect, + EuiSuperSelect, + EuiText, + EuiCallOut, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { useController } from 'react-hook-form'; +import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; +import { useUsageTracker } from '../../hooks/use_usage_tracker'; +import { ChatForm, ChatFormFields } from '../../types'; +import { AnalyticsEvents } from '../../analytics/constants'; + +export const EditContextPanel: React.FC = () => { + const usageTracker = useUsageTracker(); + const { fields } = useSourceIndicesFields(); + + const { + field: { onChange: onChangeSize, value: docSize }, + } = useController({ + name: ChatFormFields.docSize, + }); + + const { + field: { onChange: onChangeSourceFields, value: sourceFields }, + } = useController({ + name: ChatFormFields.sourceFields, + }); + + const updateSourceField = (index: string, field: string) => { + onChangeSourceFields({ + ...sourceFields, + [index]: [field], + }); + usageTracker?.click(AnalyticsEvents.editContextFieldToggled); + }; + + const handleDocSizeChange = (e: React.ChangeEvent) => { + usageTracker?.click(AnalyticsEvents.editContextDocSizeChanged); + onChangeSize(Number(e.target.value)); + }; + + return ( + + + + + + + + + + + +
    + +
    +
    +
    + {Object.entries(fields).map(([index, group], indexNum) => ( + + + {!!group.source_fields?.length ? ( + ({ + value: field, + inputDisplay: field, + 'data-test-subj': 'contextField', + }))} + valueOfSelected={sourceFields[index]?.[0]} + onChange={(value) => updateSourceField(index, value)} + fullWidth + /> + ) : ( + + )} + + + ))} +
    +
    +
    +
    + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/header.tsx b/x-pack/plugins/search_playground/public/components/header.tsx new file mode 100644 index 0000000000000..bc468a0e4be87 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/header.tsx @@ -0,0 +1,103 @@ +/* + * 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 { + EuiBetaBadge, + EuiButtonGroup, + EuiFlexGroup, + EuiPageHeaderSection, + EuiPageTemplate, + EuiTitle, + useEuiTheme, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { PlaygroundHeaderDocs } from './playground_header_docs'; +import { Toolbar } from './toolbar'; +import { ViewMode } from './app'; + +interface HeaderProps { + showDocs?: boolean; + selectedMode: string; + onModeChange: (mode: string) => void; + isActionsDisabled?: boolean; +} + +export const Header: React.FC = ({ + selectedMode, + onModeChange, + showDocs = false, + isActionsDisabled = false, +}) => { + const { euiTheme } = useEuiTheme(); + const options = [ + { + id: ViewMode.chat, + label: i18n.translate('xpack.searchPlayground.header.view.chat', { + defaultMessage: 'Chat', + }), + 'data-test-subj': 'chatMode', + }, + { + id: ViewMode.query, + label: i18n.translate('xpack.searchPlayground.header.view.query', { + defaultMessage: 'Query', + }), + 'data-test-subj': 'queryMode', + }, + ]; + + return ( + .euiFlexGroup': { flexWrap: 'wrap' }, + backgroundColor: euiTheme.colors.ghost, + }} + paddingSize="s" + data-test-subj="chat-playground-home-page" + > + + + +

    + +

    +
    + +
    +
    + + + + + + {showDocs && } + + + +
    + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx b/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx index 3aa90f188a9cf..7e94059bdc272 100644 --- a/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx +++ b/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx @@ -17,6 +17,7 @@ import { EuiSpacer, EuiText, EuiTitle, + useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -42,6 +43,7 @@ const AIMessageCSS = css` `; export const AssistantMessage: React.FC = ({ message }) => { + const { euiTheme } = useEuiTheme(); const [isDocsFlyoutOpen, setIsDocsFlyoutOpen] = useState(false); const { content, createdAt, citations, retrievalDocs, inputTokens } = message; const username = i18n.translate('xpack.searchPlayground.chat.message.assistant.username', { @@ -55,6 +57,14 @@ export const AssistantMessage: React.FC = ({ message }) = username={username} timelineAvatar="dot" data-test-subj="retrieval-docs-comment" + eventColor="subdued" + css={{ + '.euiAvatar': { backgroundColor: euiTheme.colors.ghost }, + '.euiCommentEvent': { + border: euiTheme.border.thin, + borderRadius: euiTheme.border.radius.medium, + }, + }} event={ <> @@ -96,6 +106,14 @@ export const AssistantMessage: React.FC = ({ message }) = username={username} timelineAvatar="dot" data-test-subj="retrieval-docs-comment-no-docs" + eventColor="subdued" + css={{ + '.euiAvatar': { backgroundColor: euiTheme.colors.ghost }, + '.euiCommentEvent': { + border: euiTheme.border.thin, + borderRadius: euiTheme.border.radius.medium, + }, + }} event={ <> @@ -144,6 +162,13 @@ export const AssistantMessage: React.FC = ({ message }) = }, }) } + css={{ + '.euiAvatar': { backgroundColor: euiTheme.colors.ghost }, + '.euiCommentEvent__body': { + backgroundColor: euiTheme.colors.ghost, + }, + }} + eventColor="subdued" timelineAvatar="compute" timelineAvatarAriaLabel={i18n.translate( 'xpack.searchPlayground.chat.message.assistant.avatarAriaLabel', diff --git a/x-pack/plugins/search_playground/public/components/message_list/system_message.tsx b/x-pack/plugins/search_playground/public/components/message_list/system_message.tsx index 16295299a914c..e973bea3c8a14 100644 --- a/x-pack/plugins/search_playground/public/components/message_list/system_message.tsx +++ b/x-pack/plugins/search_playground/public/components/message_list/system_message.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { EuiComment } from '@elastic/eui'; +import { EuiComment, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; interface SystemMessageProps { @@ -15,6 +15,8 @@ interface SystemMessageProps { } export const SystemMessage: React.FC = ({ content }) => { + const { euiTheme } = useEuiTheme(); + return ( = ({ content }) => { event={content} timelineAvatar="dot" eventColor="subdued" + css={{ + '.euiAvatar': { backgroundColor: euiTheme.colors.ghost }, + '.euiCommentEvent': { + border: euiTheme.border.thin, + borderRadius: euiTheme.border.radius.medium, + }, + }} /> ); }; diff --git a/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx b/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx index 12c1d86283404..8c15ca1b6b69e 100644 --- a/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx +++ b/x-pack/plugins/search_playground/public/components/message_list/user_message.tsx @@ -9,7 +9,7 @@ import React from 'react'; import moment from 'moment'; -import { EuiComment, EuiText } from '@elastic/eui'; +import { EuiComment, EuiText, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { UserAvatar } from '@kbn/user-profile-components'; @@ -26,10 +26,17 @@ const UserMessageCSS = css` `; export const UserMessage: React.FC = ({ content, createdAt }) => { + const { euiTheme } = useEuiTheme(); const currentUserProfile = useUserProfile(); return ( ( href={docLinks.chatPlayground} target="_blank" iconType="documents" + size="s" > {i18n.translate('xpack.searchPlayground.pageTitle.header.docLink', { defaultMessage: 'Playground Docs', diff --git a/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.test.tsx b/x-pack/plugins/search_playground/public/components/query_mode/query_mode.test.tsx similarity index 72% rename from x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.test.tsx rename to x-pack/plugins/search_playground/public/components/query_mode/query_mode.test.tsx index 39136e2557296..ca92bb229e38f 100644 --- a/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.test.tsx +++ b/x-pack/plugins/search_playground/public/components/query_mode/query_mode.test.tsx @@ -6,14 +6,14 @@ */ import React from 'react'; -import { render, fireEvent, screen } from '@testing-library/react'; -import { ViewQueryFlyout } from './view_query_flyout'; +import { render, screen } from '@testing-library/react'; +import { QueryMode } from './query_mode'; import { FormProvider, useForm } from 'react-hook-form'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { ChatFormFields } from '../../types'; -jest.mock('../../hooks/use_indices_fields', () => ({ - useIndicesFields: () => ({ +jest.mock('../../hooks/use_source_indices_field', () => ({ + useSourceIndicesFields: () => ({ fields: { index1: { elser_query_fields: [], @@ -30,6 +30,7 @@ jest.mock('../../hooks/use_indices_fields', () => ({ semantic_fields: [], }, }, + isFieldsLoading: false, }), })); @@ -45,6 +46,7 @@ const MockFormProvider = ({ children }: { children: React.ReactElement }) => { const methods = useForm({ values: { [ChatFormFields.indices]: ['index1', 'index2'], + [ChatFormFields.queryFields]: { index1: ['field1'], index2: ['field1'] }, [ChatFormFields.sourceFields]: { index1: ['field1'], index2: ['field1'], @@ -54,24 +56,17 @@ const MockFormProvider = ({ children }: { children: React.ReactElement }) => { return {children}; }; -describe('ViewQueryFlyout component tests', () => { - const onCloseMock = jest.fn(); - +describe('QueryMode component tests', () => { beforeEach(() => { render( - + ); }); - it('calls onClose when the close button is clicked', () => { - fireEvent.click(screen.getByTestId('euiFlyoutCloseButton')); - expect(onCloseMock).toHaveBeenCalledTimes(1); - }); - it('should see the view elasticsearch query', async () => { expect(screen.getByTestId('ViewElasticsearchQueryResult')).toBeInTheDocument(); expect(screen.getByTestId('ViewElasticsearchQueryResult')).toHaveTextContent( @@ -80,14 +75,14 @@ describe('ViewQueryFlyout component tests', () => { }); it('displays query fields and indicates hidden fields', () => { - expect(screen.getByTestId('queryFieldsSelectable_index1')).toBeInTheDocument(); - expect(screen.getByTestId('queryFieldsSelectable_index2')).toBeInTheDocument(); + expect(screen.getByTestId('fieldsAccordion-0')).toBeInTheDocument(); + expect(screen.getByTestId('fieldsAccordion-1')).toBeInTheDocument(); // Check if hidden fields indicator is shown - expect(screen.getByTestId('skipped_fields_index1')).toBeInTheDocument(); - expect(screen.getByTestId('skipped_fields_index1')).toHaveTextContent('1 fields are hidden.'); + expect(screen.getByTestId('skippedFields-0')).toBeInTheDocument(); + expect(screen.getByTestId('skippedFields-0')).toHaveTextContent('1 fields are hidden.'); // Check if hidden fields indicator is shown - expect(screen.queryByTestId('skipped_fields_index2')).not.toBeInTheDocument(); + expect(screen.queryByTestId('skippedFields-1')).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/search_playground/public/components/query_mode/query_mode.tsx b/x-pack/plugins/search_playground/public/components/query_mode/query_mode.tsx new file mode 100644 index 0000000000000..5027f0ac432a2 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/query_mode/query_mode.tsx @@ -0,0 +1,210 @@ +/* + * 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 { + EuiAccordion, + EuiBasicTable, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiLink, + EuiPanel, + EuiSpacer, + EuiSwitch, + EuiText, + useEuiTheme, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useEffect, useMemo } from 'react'; +import { useController, useWatch } from 'react-hook-form'; +import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; +import { useUsageTracker } from '../../hooks/use_usage_tracker'; +import { ChatForm, ChatFormFields } from '../../types'; +import { AnalyticsEvents } from '../../analytics/constants'; +import { docLinks } from '../../../common/doc_links'; +import { createQuery } from '../../utils/create_query'; + +const isQueryFieldSelected = ( + queryFields: ChatForm[ChatFormFields.queryFields], + index: string, + field: string +): boolean => { + return Boolean(queryFields[index]?.includes(field)); +}; + +export const QueryMode: React.FC = () => { + const { euiTheme } = useEuiTheme(); + const usageTracker = useUsageTracker(); + const { fields, isFieldsLoading } = useSourceIndicesFields(); + const sourceFields = useWatch({ + name: ChatFormFields.sourceFields, + }); + const { + field: { onChange: queryFieldsOnChange, value: queryFields }, + } = useController({ + name: ChatFormFields.queryFields, + }); + + const { + field: { onChange: elasticsearchQueryChange }, + } = useController({ + name: ChatFormFields.elasticsearchQuery, + }); + + const updateFields = (index: string, fieldName: string, checked: boolean) => { + const currentIndexFields = checked + ? [...queryFields[index], fieldName] + : queryFields[index].filter((field) => fieldName !== field); + const updatedQueryFields = { ...queryFields, [index]: currentIndexFields }; + + queryFieldsOnChange(updatedQueryFields); + elasticsearchQueryChange(createQuery(updatedQueryFields, sourceFields, fields)); + usageTracker?.count(AnalyticsEvents.queryFieldsUpdated, currentIndexFields.length); + }; + + useEffect(() => { + usageTracker?.load(AnalyticsEvents.queryModeLoaded); + }, [usageTracker]); + + const query = useMemo( + () => + !isFieldsLoading && JSON.stringify(createQuery(queryFields, sourceFields, fields), null, 2), + [isFieldsLoading, queryFields, sourceFields, fields] + ); + + return ( + + + + {query} + + + + + +
    + +
    +
    + {Object.entries(fields).map(([index, group], indexNum) => ( + + + +
    {index}
    +
    + } + initialIsOpen + data-test-subj={`fieldsAccordion-${indexNum}`} + > + + + ({ + name: typeof field === 'string' ? field : field.field, + checked: isQueryFieldSelected( + queryFields, + index, + typeof field === 'string' ? field : field.field + ), + }))} + rowHeader="name" + columns={[ + { + field: 'name', + name: 'Field', + 'data-test-subj': 'fieldName', + }, + { + field: 'checked', + name: 'Enabled', + align: 'right', + render: (checked, field) => { + return ( + updateFields(index, field.name, e.target.checked)} + compressed + /> + ); + }, + }, + ]} + /> + + {group.skipped_fields > 0 && ( + <> + + + + + + {` `} + + + + + + + + + + + )} + + + + ))} + + + + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/select_indices_flyout.test.tsx b/x-pack/plugins/search_playground/public/components/select_indices_flyout.test.tsx new file mode 100644 index 0000000000000..94dc1ad037401 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/select_indices_flyout.test.tsx @@ -0,0 +1,112 @@ +/* + * 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 React, { FC, PropsWithChildren } from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import { SelectIndicesFlyout } from './select_indices_flyout'; +import { useSourceIndicesFields } from '../hooks/use_source_indices_field'; +import { useQueryIndices } from '../hooks/use_query_indices'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +jest.mock('../hooks/use_source_indices_field'); +jest.mock('../hooks/use_query_indices'); +jest.mock('../hooks/use_indices_fields', () => ({ + useIndicesFields: () => ({ fields: {} }), +})); + +const Wrapper: FC> = ({ children }) => { + return ( + <> + {children} + + ); +}; + +const mockedUseSourceIndicesFields = useSourceIndicesFields as jest.MockedFunction< + typeof useSourceIndicesFields +>; +const mockedUseQueryIndices = useQueryIndices as jest.MockedFunction; + +describe('SelectIndicesFlyout', () => { + const onCloseMock = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + + mockedUseSourceIndicesFields.mockReturnValue({ + indices: ['index1', 'index2'], + setIndices: jest.fn(), + fields: {}, + loading: false, + addIndex: () => {}, + removeIndex: () => {}, + isFieldsLoading: false, + }); + + mockedUseQueryIndices.mockReturnValue({ + indices: ['index1', 'index2', 'index3'], + isLoading: false, + }); + }); + + it('renders correctly', () => { + const { getByTestId } = render(, { + wrapper: Wrapper, + }); + + expect(getByTestId('indicesTable')).toBeInTheDocument(); + expect(getByTestId('saveButton')).toBeInTheDocument(); + expect(getByTestId('closeButton')).toBeInTheDocument(); + }); + + it('selecting indices and saving', async () => { + const { getByTestId } = render(, { + wrapper: Wrapper, + }); + + fireEvent.click(getByTestId('sourceIndex-2')); + fireEvent.click(getByTestId('saveButton')); + + await waitFor(() => { + expect(mockedUseSourceIndicesFields().setIndices).toHaveBeenCalledWith([ + 'index1', + 'index2', + 'index3', + ]); + expect(onCloseMock).toHaveBeenCalled(); + }); + }); + + it('closing flyout without saving', () => { + const { getByTestId } = render(, { + wrapper: Wrapper, + }); + + fireEvent.click(getByTestId('closeButton')); + + expect(onCloseMock).toHaveBeenCalled(); + }); + + it('save button is disabled when no indices are selected', () => { + mockedUseSourceIndicesFields.mockReturnValueOnce({ + indices: [], + setIndices: jest.fn(), + fields: {}, + loading: false, + addIndex: () => {}, + removeIndex: () => {}, + isFieldsLoading: false, + }); + + const { getByTestId } = render(, { + wrapper: Wrapper, + }); + + const saveButton = getByTestId('saveButton'); + expect(saveButton).toBeDisabled(); + }); +}); diff --git a/x-pack/plugins/search_playground/public/components/select_indices_flyout.tsx b/x-pack/plugins/search_playground/public/components/select_indices_flyout.tsx new file mode 100644 index 0000000000000..d98a6fc9de8b7 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/select_indices_flyout.tsx @@ -0,0 +1,172 @@ +/* + * 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 React, { useState } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiSelectable, + EuiSpacer, + EuiTabbedContent, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { EuiSelectableOption } from '@elastic/eui/src/components/selectable/selectable_option'; +import { getIndicesWithNoSourceFields } from '../utils/create_query'; +import { useIndicesFields } from '../hooks/use_indices_fields'; +import { useSourceIndicesFields } from '../hooks/use_source_indices_field'; +import { useQueryIndices } from '../hooks/use_query_indices'; + +interface SelectIndicesFlyout { + onClose: () => void; +} + +export const SelectIndicesFlyout: React.FC = ({ onClose }) => { + const [query, setQuery] = useState(''); + const { indices, isLoading: isIndicesLoading } = useQueryIndices(query); + const { indices: selectedIndices, setIndices: setSelectedIndices } = useSourceIndicesFields(); + const [selectedTempIndices, setSelectedTempIndices] = useState(selectedIndices); + const handleSelectOptions = (options: EuiSelectableOption[]) => { + setSelectedTempIndices( + options.filter((option) => option.checked === 'on').map((option) => option.label) + ); + }; + const handleSearchChange = (searchValue: string) => { + setQuery(searchValue); + }; + + const handleSaveQuery = () => { + setSelectedIndices(selectedTempIndices); + onClose(); + }; + const tabs = [ + { + id: 'indices', + name: i18n.translate('xpack.searchPlayground.setupPage.addDataSource.flyout.tabName', { + defaultMessage: 'Indices', + }), + content: ( + <> + + + ({ + label: index, + checked: selectedTempIndices.includes(index) ? 'on' : '', + 'data-test-subj': `sourceIndex-${num}`, + } as EuiSelectableOption) + ), + ]} + onChange={handleSelectOptions} + listProps={{ + showIcons: true, + bordered: false, + }} + isLoading={isIndicesLoading} + renderOption={undefined} + > + {(list, search) => ( + <> + {search} + {list} + + )} + + + ), + }, + ]; + const { fields, isLoading: isFieldsLoading } = useIndicesFields(selectedTempIndices); + const noSourceFieldsWarning = getIndicesWithNoSourceFields(fields); + + return ( + + + +

    + +

    +
    +
    + + + {!isFieldsLoading && !!noSourceFieldsWarning && ( + +

    + +

    +
    + )} +
    + + + + + + + + + + + + + + +
    + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/set_up_connector_panel_for_start_chat.tsx b/x-pack/plugins/search_playground/public/components/set_up_connector_panel_for_start_chat.tsx deleted file mode 100644 index 2430cd4e6c7a0..0000000000000 --- a/x-pack/plugins/search_playground/public/components/set_up_connector_panel_for_start_chat.tsx +++ /dev/null @@ -1,111 +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 React, { useEffect, useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { GenerativeAIForSearchPlaygroundConnectorFeatureId } from '@kbn/actions-plugin/common'; -import { AnalyticsEvents } from '../analytics/constants'; -import { useUsageTracker } from '../hooks/use_usage_tracker'; -import { useKibana } from '../hooks/use_kibana'; -import { useLoadConnectors } from '../hooks/use_load_connectors'; -import { StartChatPanel } from './start_chat_panel'; - -export const SetUpConnectorPanelForStartChat: React.FC = () => { - const [connectorFlyoutOpen, setConnectorFlyoutOpen] = useState(false); - const [showCallout, setShowAddedCallout] = useState(false); - const { - services: { - triggersActionsUi: { getAddConnectorFlyout: ConnectorFlyout }, - }, - } = useKibana(); - const { - data: connectors, - refetch: refetchConnectors, - isLoading: isConnectorListLoading, - } = useLoadConnectors(); - const usageTracker = useUsageTracker(); - const handleConnectorCreated = () => { - refetchConnectors(); - setShowAddedCallout(true); - setConnectorFlyoutOpen(false); - }; - const handleSetupGenAiConnector = () => { - usageTracker?.click(AnalyticsEvents.genAiConnectorCreated); - setConnectorFlyoutOpen(true); - }; - - useEffect(() => { - if (connectors?.length) { - if (showCallout) { - usageTracker?.load(AnalyticsEvents.genAiConnectorAdded); - } else { - usageTracker?.load(AnalyticsEvents.genAiConnectorExists); - } - } else { - usageTracker?.load(AnalyticsEvents.genAiConnectorSetup); - } - }, [connectors?.length, showCallout, usageTracker]); - - if (isConnectorListLoading) { - return null; - } - - return connectors?.length ? ( - showCallout ? ( - - ) : null - ) : ( - <> - - } - dataTestSubj="connectToLLMChatPanel" - > - - - - - - - - - {connectorFlyoutOpen && ( - setConnectorFlyoutOpen(false)} - /> - )} - - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/setup_page/add_data_sources.tsx b/x-pack/plugins/search_playground/public/components/setup_page/add_data_sources.tsx new file mode 100644 index 0000000000000..77d8114866592 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/setup_page/add_data_sources.tsx @@ -0,0 +1,53 @@ +/* + * 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 React, { useState } from 'react'; +import { useFormContext } from 'react-hook-form'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; +import { ChatForm, ChatFormFields } from '../../types'; +import { SelectIndicesFlyout } from '../select_indices_flyout'; + +export const AddDataSources: React.FC = () => { + const [showFlyout, setShowFlyout] = useState(false); + const { getValues } = useFormContext(); + const hasSelectedIndices: boolean = !!getValues(ChatFormFields.indices)?.length; + const handleFlyoutClose = () => { + setShowFlyout(false); + }; + + return ( + <> + {showFlyout && } + {hasSelectedIndices ? ( + setShowFlyout(true)} + data-test-subj="dataSourcesSuccessButton" + > + + + ) : ( + setShowFlyout(true)} + data-test-subj="addDataSourcesButton" + > + + + )} + + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/set_up_connector_panel_for_start_chat.test.tsx b/x-pack/plugins/search_playground/public/components/setup_page/connect_llm_button.test.tsx similarity index 66% rename from x-pack/plugins/search_playground/public/components/set_up_connector_panel_for_start_chat.test.tsx rename to x-pack/plugins/search_playground/public/components/setup_page/connect_llm_button.test.tsx index 7ed41e946a91f..65be856d77eb3 100644 --- a/x-pack/plugins/search_playground/public/components/set_up_connector_panel_for_start_chat.test.tsx +++ b/x-pack/plugins/search_playground/public/components/setup_page/connect_llm_button.test.tsx @@ -7,17 +7,17 @@ import React from 'react'; import { fireEvent, render as testingLibraryRender, waitFor } from '@testing-library/react'; -import { SetUpConnectorPanelForStartChat } from './set_up_connector_panel_for_start_chat'; -import { useKibana } from '../hooks/use_kibana'; -import { useLoadConnectors } from '../hooks/use_load_connectors'; +import { ConnectLLMButton } from './connect_llm_button'; +import { useKibana } from '../../hooks/use_kibana'; +import { useLoadConnectors } from '../../hooks/use_load_connectors'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; const render = (children: React.ReactNode) => testingLibraryRender({children}); -jest.mock('../hooks/use_kibana'); -jest.mock('../hooks/use_load_connectors'); -jest.mock('../hooks/use_usage_tracker', () => ({ +jest.mock('../../hooks/use_kibana'); +jest.mock('../../hooks/use_load_connectors'); +jest.mock('../../hooks/use_usage_tracker', () => ({ useUsageTracker: () => ({ count: jest.fn(), load: jest.fn(), @@ -30,7 +30,7 @@ const mockConnectors = { '2': { title: 'Connector 2' }, }; -describe('SetUpConnectorPanelForStartChat', () => { +describe('ConnectLLMButton', () => { beforeEach(() => { (useKibana as jest.Mock).mockReturnValue({ services: { @@ -59,8 +59,8 @@ describe('SetUpConnectorPanelForStartChat', () => { isLoading: false, isSuccess: true, }); - const { getByTestId } = render(); - expect(getByTestId('setupGenAIConnectorButton')).toBeInTheDocument(); + const { getByTestId } = render(); + expect(getByTestId('connectLLMButton')).toBeInTheDocument(); }); it('show the flyout when the button is clicked', async () => { @@ -69,11 +69,22 @@ describe('SetUpConnectorPanelForStartChat', () => { isLoading: false, isSuccess: true, }); - const { getByTestId, queryByTestId } = render(); + const { getByTestId, queryByTestId } = render(); expect(queryByTestId('addConnectorFlyout')).not.toBeInTheDocument(); - fireEvent.click(getByTestId('setupGenAIConnectorButton')); + fireEvent.click(getByTestId('connectLLMButton')); await waitFor(() => expect(getByTestId('addConnectorFlyout')).toBeInTheDocument()); }); + + it('show success button when connector exists', async () => { + (useLoadConnectors as jest.Mock).mockReturnValue({ + data: [{}], + isLoading: false, + isSuccess: true, + }); + const { queryByTestId } = render(); + + expect(queryByTestId('successConnectLLMButton')).toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/search_playground/public/components/setup_page/connect_llm_button.tsx b/x-pack/plugins/search_playground/public/components/setup_page/connect_llm_button.tsx new file mode 100644 index 0000000000000..579715e79ea55 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/setup_page/connect_llm_button.tsx @@ -0,0 +1,85 @@ +/* + * 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 { EuiButton, EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useEffect, useState } from 'react'; +import { GenerativeAIForSearchPlaygroundConnectorFeatureId } from '@kbn/actions-plugin/common'; +import { useKibana } from '../../hooks/use_kibana'; +import { useLoadConnectors } from '../../hooks/use_load_connectors'; +import { useUsageTracker } from '../../hooks/use_usage_tracker'; +import { AnalyticsEvents } from '../../analytics/constants'; + +export const ConnectLLMButton: React.FC = () => { + const [connectorFlyoutOpen, setConnectorFlyoutOpen] = useState(false); + const [showCallout, setShowAddedCallout] = useState(false); + const { + services: { + triggersActionsUi: { getAddConnectorFlyout: ConnectorFlyout }, + }, + } = useKibana(); + const { data: connectors, refetch: refetchConnectors } = useLoadConnectors(); + const usageTracker = useUsageTracker(); + const handleConnectorCreated = () => { + refetchConnectors(); + setShowAddedCallout(true); + setConnectorFlyoutOpen(false); + }; + const handleSetupGenAiConnector = () => { + usageTracker?.click(AnalyticsEvents.genAiConnectorCreated); + setConnectorFlyoutOpen(true); + }; + + useEffect(() => { + if (connectors?.length) { + if (showCallout) { + usageTracker?.load(AnalyticsEvents.genAiConnectorAdded); + } else { + usageTracker?.load(AnalyticsEvents.genAiConnectorExists); + } + } else { + usageTracker?.load(AnalyticsEvents.genAiConnectorSetup); + } + }, [connectors?.length, showCallout, usageTracker]); + + return ( + <> + {connectors?.length ? ( + + + + ) : ( + + + + )} + {connectorFlyoutOpen && ( + setConnectorFlyoutOpen(false)} + /> + )} + + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.test.tsx b/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx similarity index 87% rename from x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.test.tsx rename to x-pack/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx index d75d5385cc3f8..485bb5eb5df55 100644 --- a/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.test.tsx +++ b/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx @@ -7,9 +7,9 @@ import React, { FC, PropsWithChildren } from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react'; -import { CreateIndexCallout } from './create_index_callout'; import { useKibana } from '../../hooks/use_kibana'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { CreateIndexButton } from './create_index_button'; // Mocking the useKibana hook jest.mock('../../hooks/use_kibana', () => ({ @@ -37,9 +37,9 @@ const Wrapper: FC> = ({ children }) => { ); }; -describe('CreateIndexCallout', () => { +describe('CreateIndexButton', () => { it('renders correctly when there is no locator', async () => { - const { queryByTestId } = render(, { wrapper: Wrapper }); + const { queryByTestId } = render(, { wrapper: Wrapper }); expect(queryByTestId('createIndexButton')).not.toBeInTheDocument(); }); @@ -64,7 +64,7 @@ describe('CreateIndexCallout', () => { }, })); - const { getByTestId } = render(, { wrapper: Wrapper }); + const { getByTestId } = render(, { wrapper: Wrapper }); const createIndexButton = getByTestId('createIndexButton'); expect(createIndexButton).toBeInTheDocument(); diff --git a/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.tsx b/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.tsx new file mode 100644 index 0000000000000..d4a165f56266c --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.tsx @@ -0,0 +1,53 @@ +/* + * 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 { EuiButton, EuiCallOut } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { useKibana } from '../../hooks/use_kibana'; + +export const CreateIndexButton: React.FC = () => { + const { + services: { application, share }, + } = useKibana(); + const createIndexLocator = useMemo( + () => share.url.locators.get('CREATE_INDEX_LOCATOR_ID'), + [share.url.locators] + ); + const handleNavigateToIndex = useCallback(async () => { + const createIndexUrl = await createIndexLocator?.getUrl({}); + + if (createIndexUrl) { + application?.navigateToUrl(createIndexUrl); + } + }, [application, createIndexLocator]); + + return createIndexLocator ? ( + + + + ) : ( + + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/setup_page/setup_page.tsx b/x-pack/plugins/search_playground/public/components/setup_page/setup_page.tsx new file mode 100644 index 0000000000000..6dc4d80cc8b15 --- /dev/null +++ b/x-pack/plugins/search_playground/public/components/setup_page/setup_page.tsx @@ -0,0 +1,109 @@ +/* + * 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 { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiLoadingSpinner, + EuiTitle, +} from '@elastic/eui'; +import React, { useEffect, useMemo } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useSearchParams } from 'react-router-dom-v5-compat'; +import { CreateIndexButton } from './create_index_button'; +import { useQueryIndices } from '../../hooks/use_query_indices'; +import { docLinks } from '../../../common/doc_links'; +import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; +import { useUsageTracker } from '../../hooks/use_usage_tracker'; +import { AnalyticsEvents } from '../../analytics/constants'; +import { ConnectLLMButton } from './connect_llm_button'; +import { AddDataSources } from './add_data_sources'; + +export const SetupPage: React.FC = () => { + const usageTracker = useUsageTracker(); + const [searchParams] = useSearchParams(); + const { indices, isLoading: isIndicesLoading } = useQueryIndices(); + const index = useMemo(() => searchParams.get('default-index'), [searchParams]); + const { addIndex } = useSourceIndicesFields(); + + useEffect(() => { + if (index) { + addIndex(index); + } + }, [index, addIndex]); + + useEffect(() => { + usageTracker?.load(AnalyticsEvents.setupChatPageLoaded); + }, [usageTracker]); + + return ( + + + + } + body={ + <> +

    + +

    +

    + +

    + + } + actions={ + + {isIndicesLoading ? ( + + ) : ( + <> + + + + + {indices.length ? : } + + + )} + + } + footer={ + <> + + + + + {' '} + + + + + } + /> + ); +}; diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/add_indices_field.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/add_indices_field.tsx deleted file mode 100644 index 786a9db0c8238..0000000000000 --- a/x-pack/plugins/search_playground/public/components/sources_panel/add_indices_field.tsx +++ /dev/null @@ -1,65 +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 { EuiComboBox, EuiFormRow } from '@elastic/eui'; -import React, { useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; -import { IndexName } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { useQueryIndices } from '../../hooks/use_query_indices'; - -interface AddIndicesFieldProps { - selectedIndices: IndexName[]; - onIndexSelect: (index: IndexName) => void; - loading: boolean; -} - -export const AddIndicesField: React.FC = ({ - selectedIndices, - onIndexSelect, - loading, -}) => { - const [query, setQuery] = useState(''); - const { indices, isLoading } = useQueryIndices(query); - const handleChange = (value: Array>) => { - if (value?.[0]?.label) { - onIndexSelect(value[0].label); - } - }; - const handleSearchChange = (searchValue: string) => { - setQuery(searchValue); - }; - - return ( - - ({ - label: index, - disabled: selectedIndices.includes(index), - }))} - isClearable={false} - data-test-subj="selectIndicesComboBox" - /> - - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.tsx deleted file mode 100644 index 4294272946f76..0000000000000 --- a/x-pack/plugins/search_playground/public/components/sources_panel/create_index_callout.tsx +++ /dev/null @@ -1,65 +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 { EuiButton, EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useCallback, useMemo } from 'react'; -import { useKibana } from '../../hooks/use_kibana'; - -export const CreateIndexCallout: React.FC = () => { - const { - services: { application, share }, - } = useKibana(); - const createIndexLocator = useMemo( - () => share.url.locators.get('CREATE_INDEX_LOCATOR_ID'), - [share.url.locators] - ); - const handleNavigateToIndex = useCallback(async () => { - const createIndexUrl = await createIndexLocator?.getUrl({}); - - if (createIndexUrl) { - application?.navigateToUrl(createIndexUrl); - } - }, [application, createIndexLocator]); - - return ( - - -

    - -

    -
    - - {createIndexLocator && ( - - - - )} -
    - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_sidebar.test.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_sidebar.test.tsx deleted file mode 100644 index 194b451ccd1fb..0000000000000 --- a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_sidebar.test.tsx +++ /dev/null @@ -1,73 +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 React, { FC, PropsWithChildren } from 'react'; -import { render, screen } from '@testing-library/react'; -import { SourcesPanelSidebar } from './sources_panel_sidebar'; -import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; -import { useQueryIndices } from '../../hooks/use_query_indices'; -import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; - -jest.mock('../../hooks/use_source_indices_field', () => ({ - useSourceIndicesFields: jest.fn(), -})); -jest.mock('../../hooks/use_query_indices', () => ({ - useQueryIndices: jest.fn(), -})); - -const Wrapper: FC> = ({ children }) => { - return ( - <> - {children} - - ); -}; - -describe('SourcesPanelSidebar component', () => { - afterEach(jest.clearAllMocks); - - it('shows the "AddIndicesField" component when there are indices and not loading', () => { - (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: [], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - }); - - render(, { wrapper: Wrapper }); - expect(screen.queryByTestId('indicesLoading')).not.toBeInTheDocument(); - }); - - it('displays IndicesTable when there are selected indices', () => { - (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: ['index1', 'index2'], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - }); - - render(, { wrapper: Wrapper }); - expect(screen.getAllByTestId('removeIndexButton')).toHaveLength(2); - expect(screen.getAllByTestId('removeIndexButton')[0]).not.toBeDisabled(); - expect(screen.getAllByTestId('removeIndexButton')[1]).not.toBeDisabled(); - }); - - it('does not allow to remove all indices', () => { - (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: ['index1'], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - }); - - render(, { wrapper: Wrapper }); - expect(screen.getByTestId('removeIndexButton')).toBeDisabled(); - }); -}); diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.test.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.test.tsx deleted file mode 100644 index 72c0aadfdb708..0000000000000 --- a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.test.tsx +++ /dev/null @@ -1,108 +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 React, { FC, PropsWithChildren } from 'react'; -import { render, screen } from '@testing-library/react'; -import { SourcesPanelForStartChat } from './sources_panel_for_start_chat'; -import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; -import { useQueryIndices } from '../../hooks/use_query_indices'; -import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; - -jest.mock('../../hooks/use_source_indices_field', () => ({ - useSourceIndicesFields: jest.fn(), -})); -jest.mock('../../hooks/use_query_indices', () => ({ - useQueryIndices: jest.fn(), -})); - -jest.mock('../../hooks/use_kibana', () => ({ - useKibana: jest.fn(() => ({ - services: { - application: { navigateToUrl: jest.fn() }, - share: { url: { locators: { get: jest.fn() } } }, - }, - })), -})); - -const Wrapper: FC> = ({ children }) => { - return ( - <> - {children} - - ); -}; - -describe('SourcesPanelForStartChat component', () => { - afterEach(jest.clearAllMocks); - - it('shows a loading spinner when query is loading', () => { - (useQueryIndices as jest.Mock).mockReturnValue({ indices: [], isLoading: true }); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: [], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - }); - - render(, { wrapper: Wrapper }); - expect(screen.getByTestId('indicesLoading')).toBeInTheDocument(); - }); - - it('shows the "AddIndicesField" component when there are indices and not loading', () => { - (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: [], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - }); - - render(, { wrapper: Wrapper }); - expect(screen.queryByTestId('indicesLoading')).not.toBeInTheDocument(); - }); - - it('displays IndicesTable when there are selected indices', () => { - (useQueryIndices as jest.Mock).mockReturnValue({ indices: ['index1'], isLoading: false }); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: ['index1'], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - }); - - render(, { wrapper: Wrapper }); - expect(screen.getAllByText('index1')).toHaveLength(1); - expect(screen.getByTestId('removeIndexButton')).toBeInTheDocument(); - }); - - it('displays "CreateIndexCallout" when no indices are found and not loading', () => { - (useQueryIndices as jest.Mock).mockReturnValue({ indices: [], isLoading: false }); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: [], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - }); - - render(, { wrapper: Wrapper }); - expect(screen.getByTestId('createIndexCallout')).toBeInTheDocument(); - }); - - it('renders warning callout', () => { - (useSourceIndicesFields as jest.Mock).mockReturnValue({ - indices: ['index1'], - removeIndex: jest.fn(), - addIndex: jest.fn(), - loading: false, - noFieldsIndicesWarning: 'index1', - }); - - render(, { wrapper: Wrapper }); - expect(screen.getByTestId('NoIndicesFieldsMessage')).toBeInTheDocument(); - expect(screen.getByTestId('NoIndicesFieldsMessage')).toHaveTextContent('index1'); - }); -}); diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.tsx deleted file mode 100644 index 992ae7e574658..0000000000000 --- a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_for_start_chat.tsx +++ /dev/null @@ -1,79 +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 { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { AddIndicesField } from './add_indices_field'; -import { IndicesTable } from './indices_table'; -import { StartChatPanel } from '../start_chat_panel'; -import { CreateIndexCallout } from './create_index_callout'; -import { useQueryIndices } from '../../hooks/use_query_indices'; -import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; - -export const SourcesPanelForStartChat: React.FC = () => { - const { - indices: selectedIndices, - removeIndex, - addIndex, - loading: fieldIndicesLoading, - noFieldsIndicesWarning, - } = useSourceIndicesFields(); - const { indices, isLoading } = useQueryIndices(); - - return ( - - {!!selectedIndices?.length && ( - - - - )} - - {noFieldsIndicesWarning && ( - -

    - {i18n.translate('xpack.searchPlayground.emptyPrompts.sources.warningCallout', { - defaultMessage: - 'No fields found for {errorMessage}. Try adding data to these indices.', - values: { - errorMessage: noFieldsIndicesWarning, - }, - })} -

    -
    - )} - - {isLoading && ( - - - - )} - - {!isLoading && !!indices?.length && ( - - - - )} - - {!isLoading && !indices?.length && } -
    - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_sidebar.tsx b/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_sidebar.tsx deleted file mode 100644 index 813c98641ff29..0000000000000 --- a/x-pack/plugins/search_playground/public/components/sources_panel/sources_panel_sidebar.tsx +++ /dev/null @@ -1,43 +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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { useSourceIndicesFields } from '../../hooks/use_source_indices_field'; -import { AddIndicesField } from './add_indices_field'; -import { IndicesList } from './indices_list'; - -export const SourcesPanelSidebar: React.FC = () => { - const { indices: selectedIndices, removeIndex, addIndex, loading } = useSourceIndicesFields(); - - return ( - - - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/start_chat_panel.tsx b/x-pack/plugins/search_playground/public/components/start_chat_panel.tsx deleted file mode 100644 index 996f088090d29..0000000000000 --- a/x-pack/plugins/search_playground/public/components/start_chat_panel.tsx +++ /dev/null @@ -1,69 +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 { - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPanel, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import React, { FC, PropsWithChildren } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; - -interface StartChatPanelProps { - children: React.ReactNode; - title: string; - description: string | React.ReactNode; - isValid?: boolean; - dataTestSubj: string; -} - -export const StartChatPanel: FC> = ({ - title, - description, - children, - isValid, - dataTestSubj, -}) => ( - - - -
    {title}
    -
    - - {isValid && ( - - - - - -

    - -

    -
    -
    -
    - )} -
    - - - - - -

    {description}

    -
    - - {children} -
    -
    -); diff --git a/x-pack/plugins/search_playground/public/components/start_new_chat.test.tsx b/x-pack/plugins/search_playground/public/components/start_new_chat.test.tsx deleted file mode 100644 index ed380e00eb1da..0000000000000 --- a/x-pack/plugins/search_playground/public/components/start_new_chat.test.tsx +++ /dev/null @@ -1,123 +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 { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; - -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { MemoryRouter, useSearchParams } from 'react-router-dom-v5-compat'; -import { StartNewChat } from './start_new_chat'; -import { useLoadConnectors } from '../hooks/use_load_connectors'; -import { useUsageTracker } from '../hooks/use_usage_tracker'; -import { AnalyticsEvents } from '../analytics/constants'; -import { useSourceIndicesFields } from '../hooks/use_source_indices_field'; -import { useKibana } from '../hooks/use_kibana'; -import { PlaygroundProvider } from '../providers/playground_provider'; - -jest.mock('../hooks/use_load_connectors'); -jest.mock('../hooks/use_usage_tracker'); -jest.mock('../hooks/use_source_indices_field', () => ({ - useSourceIndicesFields: jest.fn(() => ({ addIndex: jest.fn() })), -})); -jest.mock('react-router-dom-v5-compat', () => ({ - ...jest.requireActual('react-router-dom-v5-compat'), - useSearchParams: jest.fn(), -})); -jest.mock('../hooks/use_kibana'); - -const mockUseLoadConnectors = useLoadConnectors as jest.Mock; -const mockUseUsageTracker = useUsageTracker as jest.Mock; -const mockUseSearchParams = useSearchParams as jest.Mock; - -const renderWithForm = (ui: React.ReactElement) => { - const Wrapper: React.FC = ({ children }) => { - return ( - - {children} - - ); - }; - return render(ui, { wrapper: Wrapper }); -}; - -const mockConnectors = { - '1': { title: 'Connector 1' }, - '2': { title: 'Connector 2' }, -}; - -describe('StartNewChat', () => { - beforeEach(() => { - mockUseLoadConnectors.mockReturnValue({ data: [] }); - mockUseUsageTracker.mockReturnValue({ load: jest.fn() }); - mockUseSearchParams.mockReturnValue([new URLSearchParams()]); - - (useKibana as jest.Mock).mockReturnValue({ - services: { - triggersActionsUi: { - getAddConnectorFlyout: () => ( -
    Add Connector Flyout
    - ), - }, - }, - }); - (useLoadConnectors as jest.Mock).mockReturnValue({ - data: mockConnectors, - refetch: jest.fn(), - isLoading: false, - isSuccess: true, - }); - }); - - it('renders correctly', () => { - renderWithForm( - - - - ); - - expect(screen.getByTestId('startNewChatTitle')).toBeInTheDocument(); - }); - - it('disables the start button when form conditions are not met', () => { - renderWithForm( - - - - ); - - const startButton = screen.getByTestId('startChatButton'); - expect(startButton).toBeDisabled(); - }); - - it('tracks the page load event', () => { - const usageTracker = { load: jest.fn() }; - mockUseUsageTracker.mockReturnValue(usageTracker); - - renderWithForm( - - - - ); - - expect(usageTracker.load).toHaveBeenCalledWith(AnalyticsEvents.startNewChatPageLoaded); - }); - - it('calls addIndex when default-index is present in searchParams', () => { - const addIndex = jest.fn(); - (useSourceIndicesFields as jest.Mock).mockReturnValue({ addIndex }); - const searchParams = new URLSearchParams({ 'default-index': 'test-index' }); - mockUseSearchParams.mockReturnValue([searchParams]); - - renderWithForm( - - - - ); - - expect(addIndex).toHaveBeenCalledWith('test-index'); - }); -}); diff --git a/x-pack/plugins/search_playground/public/components/start_new_chat.tsx b/x-pack/plugins/search_playground/public/components/start_new_chat.tsx deleted file mode 100644 index 0df4485ec858c..0000000000000 --- a/x-pack/plugins/search_playground/public/components/start_new_chat.tsx +++ /dev/null @@ -1,123 +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 { - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiText, - EuiTitle, - useEuiTheme, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useEffect, useMemo } from 'react'; -import { useFormContext } from 'react-hook-form'; -import { useSearchParams } from 'react-router-dom-v5-compat'; -import { useUsageTracker } from '../hooks/use_usage_tracker'; -import { useLoadConnectors } from '../hooks/use_load_connectors'; -import { SourcesPanelForStartChat } from './sources_panel/sources_panel_for_start_chat'; -import { SetUpConnectorPanelForStartChat } from './set_up_connector_panel_for_start_chat'; -import { ChatFormFields } from '../types'; -import { AnalyticsEvents } from '../analytics/constants'; -import { useSourceIndicesFields } from '../hooks/use_source_indices_field'; - -const maxWidthPage = 640; - -interface StartNewChatProps { - onStartClick: () => void; -} - -export const StartNewChat: React.FC = ({ onStartClick }) => { - const { euiTheme } = useEuiTheme(); - const { data: connectors } = useLoadConnectors(); - const { watch } = useFormContext(); - const usageTracker = useUsageTracker(); - - const [searchParams] = useSearchParams(); - const index = useMemo(() => searchParams.get('default-index'), [searchParams]); - const { addIndex } = useSourceIndicesFields(); - - useEffect(() => { - if (index) { - addIndex(index); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [index]); - - useEffect(() => { - usageTracker?.load(AnalyticsEvents.startNewChatPageLoaded); - }, [usageTracker]); - - return ( - - - - - -

    - -

    -
    - - -
    -
    - - -

    - -

    -
    -
    - - - - - - - - - - - - - - -
    -
    - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/summarization_panel/include_citations_field.tsx b/x-pack/plugins/search_playground/public/components/summarization_panel/include_citations_field.tsx index 75212a2ba9cc3..6ab49bea16c3d 100644 --- a/x-pack/plugins/search_playground/public/components/summarization_panel/include_citations_field.tsx +++ b/x-pack/plugins/search_playground/public/components/summarization_panel/include_citations_field.tsx @@ -27,7 +27,7 @@ export const IncludeCitationsField: React.FC = ({ }; return ( - + = ({ value, onC } + fullWidth > { ); expect(getByTestId('summarizationModelSelect')).toBeInTheDocument(); - expect(getByTestId('manageConnectorsLink')).toHaveAttribute( - 'href', - 'http://example.com/manage-connectors' - ); }); }); diff --git a/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx b/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx index f60436025e0d1..e13823d87fd8d 100644 --- a/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx +++ b/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_model.tsx @@ -8,7 +8,6 @@ import React, { useEffect, useMemo } from 'react'; import { - EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow, @@ -16,18 +15,15 @@ import { EuiSuperSelect, type EuiSuperSelectOption, EuiText, - EuiToolTip, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { AnalyticsEvents } from '../../analytics/constants'; import { useUsageTracker } from '../../hooks/use_usage_tracker'; import type { LLMModel } from '../../types'; -import { useManagementLink } from '../../hooks/use_management_link'; interface SummarizationModelProps { - selectedModel: LLMModel; + selectedModel?: LLMModel; onSelect: (model: LLMModel) => void; models: LLMModel[]; } @@ -40,7 +36,6 @@ export const SummarizationModel: React.FC = ({ onSelect, }) => { const usageTracker = useUsageTracker(); - const managementLink = useManagementLink(selectedModel.connectorId); const onChange = (modelValue: string) => { const newSelectedModel = models.find((model) => getOptionValue(model) === modelValue); @@ -98,7 +93,7 @@ export const SummarizationModel: React.FC = ({ useEffect(() => { usageTracker?.click( - `${AnalyticsEvents.modelSelected}_${selectedModel.value || selectedModel.connectorType}` + `${AnalyticsEvents.modelSelected}_${selectedModel!.value || selectedModel!.connectorType}` ); }, [usageTracker, selectedModel]); @@ -113,36 +108,14 @@ export const SummarizationModel: React.FC = ({ />{' '} } - labelAppend={ - - - - } + fullWidth > ); diff --git a/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_panel.tsx b/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_panel.tsx index 77b152121f5b0..4a199f7d98b18 100644 --- a/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_panel.tsx +++ b/x-pack/plugins/search_playground/public/components/summarization_panel/summarization_panel.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { Controller, useFormContext } from 'react-hook-form'; +import { EuiPanel } from '@elastic/eui'; import { useLLMsModels } from '../../hooks/use_llms_models'; import { IncludeCitationsField } from './include_citations_field'; import { InstructionsField } from './instructions_field'; @@ -17,13 +18,11 @@ import { SummarizationModel } from './summarization_model'; export const SummarizationPanel: React.FC = () => { const { control } = useFormContext(); const models = useLLMsModels(); - const defaultModel = models.find((model) => !model.disabled); return ( - <> + ( @@ -51,6 +50,6 @@ export const SummarizationPanel: React.FC = () => { )} /> - + ); }; diff --git a/x-pack/plugins/search_playground/public/components/toolbar.tsx b/x-pack/plugins/search_playground/public/components/toolbar.tsx index e322c9c6bb3ff..31ea3345cdcbe 100644 --- a/x-pack/plugins/search_playground/public/components/toolbar.tsx +++ b/x-pack/plugins/search_playground/public/components/toolbar.tsx @@ -7,15 +7,13 @@ import { EuiFlexGroup } from '@elastic/eui'; import React from 'react'; +import { DataActionButton } from './data_action_button'; import { ViewCodeAction } from './view_code/view_code_action'; -import { ViewQueryAction } from './view_query/view_query_action'; -import { EditContextAction } from './edit_context/edit_context_action'; export const Toolbar: React.FC = () => { return ( - - - + + ); diff --git a/x-pack/plugins/search_playground/public/components/view_code/view_code_action.tsx b/x-pack/plugins/search_playground/public/components/view_code/view_code_action.tsx index 01ddc3d789dc5..13b6a2119757a 100644 --- a/x-pack/plugins/search_playground/public/components/view_code/view_code_action.tsx +++ b/x-pack/plugins/search_playground/public/components/view_code/view_code_action.tsx @@ -27,6 +27,7 @@ export const ViewCodeAction: React.FC = () => { onClick={() => setShowFlyout(true)} disabled={!selectedIndices || selectedIndices?.length === 0} data-test-subj="viewCodeActionButton" + size="s" > { - const [showFlyout, setShowFlyout] = useState(false); - const { watch } = useFormContext(); - const selectedIndices: string[] = watch(ChatFormFields.indices); - - return ( - <> - {showFlyout && setShowFlyout(false)} />} - setShowFlyout(true)} - disabled={selectedIndices?.length === 0} - data-test-subj="viewQueryActionButton" - > - - - - ); -}; diff --git a/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.tsx b/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.tsx deleted file mode 100644 index 2fd64f073eac7..0000000000000 --- a/x-pack/plugins/search_playground/public/components/view_query/view_query_flyout.tsx +++ /dev/null @@ -1,302 +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 { - EuiAccordion, - EuiSelectable, - EuiButton, - EuiButtonEmpty, - EuiCodeBlock, - EuiFlexGroup, - EuiFlexItem, - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlyoutHeader, - EuiPanel, - EuiSpacer, - EuiSelectableOption, - EuiText, - EuiTitle, - EuiCheckbox, - EuiLink, - EuiIcon, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useEffect, useState } from 'react'; -import { useFormContext } from 'react-hook-form'; -import { useController } from 'react-hook-form'; -import { AnalyticsEvents } from '../../analytics/constants'; -import { docLinks } from '../../../common/doc_links'; -import { useIndicesFields } from '../../hooks/use_indices_fields'; -import { useUsageTracker } from '../../hooks/use_usage_tracker'; -import { ChatForm, ChatFormFields, IndicesQuerySourceFields } from '../../types'; -import { createQuery, getDefaultQueryFields, IndexFields } from '../../utils/create_query'; - -const groupTypeQueryFields = ( - fields: IndicesQuerySourceFields, - queryFields: IndexFields -): string[] => - Object.entries(queryFields).map(([index, selectedFields]) => { - const indexFields = fields[index]; - let typeQueryFields = ''; - - if (selectedFields.some((field) => indexFields.bm25_query_fields.includes(field))) { - typeQueryFields = 'BM25'; - } - - if ( - selectedFields.some((field) => - indexFields.dense_vector_query_fields.find((vectorField) => vectorField.field === field) - ) - ) { - typeQueryFields += (typeQueryFields ? '_' : '') + 'DENSE'; - } - - if ( - selectedFields.some((field) => - indexFields.elser_query_fields.find((elserField) => elserField.field === field) - ) - ) { - typeQueryFields += (typeQueryFields ? '_' : '') + 'SPARSE'; - } - - if ( - selectedFields.some((field) => indexFields.semantic_fields.find((f) => f.field === field)) - ) { - typeQueryFields += (typeQueryFields ? '_' : '') + 'SEMANTIC'; - } - - return typeQueryFields; - }); - -interface ViewQueryFlyoutProps { - onClose: () => void; -} - -export const ViewQueryFlyout: React.FC = ({ onClose }) => { - const usageTracker = useUsageTracker(); - const { getValues } = useFormContext(); - const selectedIndices: string[] = getValues(ChatFormFields.indices); - const sourceFields = getValues(ChatFormFields.sourceFields); - const { fields } = useIndicesFields(selectedIndices); - const defaultFields = getDefaultQueryFields(fields); - - const { - field: { onChange: queryFieldsOnChange, value: queryFields }, - } = useController({ - name: ChatFormFields.queryFields, - defaultValue: defaultFields, - }); - - const [tempQueryFields, setTempQueryFields] = useState(queryFields); - - const { - field: { onChange: elasticsearchQueryChange }, - } = useController({ - name: ChatFormFields.elasticsearchQuery, - }); - - const isQueryFieldSelected = (index: string, field: string) => { - return tempQueryFields[index].includes(field); - }; - - const updateFields = (index: string, options: EuiSelectableOption[]) => { - const newFields = options - .filter((option) => option.checked === 'on') - .map((option) => option.label); - setTempQueryFields({ - ...tempQueryFields, - [index]: newFields, - }); - usageTracker?.count(AnalyticsEvents.viewQueryFieldsUpdated, newFields.length); - }; - - const saveQuery = () => { - queryFieldsOnChange(tempQueryFields); - elasticsearchQueryChange(createQuery(tempQueryFields, sourceFields, fields)); - onClose(); - - const groupedQueryFields = groupTypeQueryFields(fields, tempQueryFields); - - groupedQueryFields.forEach((typeQueryFields) => - usageTracker?.click(`${AnalyticsEvents.viewQuerySaved}_${typeQueryFields}`) - ); - }; - - useEffect(() => { - usageTracker?.load(AnalyticsEvents.viewQueryFlyoutOpened); - }, [usageTracker]); - - return ( - - - -

    - -

    -
    - - -

    - - {` `} - - - -

    -
    -
    - - - - - {JSON.stringify(createQuery(tempQueryFields, sourceFields, fields), null, 2)} - - - - - -
    - -
    -
    - {Object.entries(fields).map(([index, group]) => ( - - - -
    {index}
    -
    - } - > - - { - const checked = isQueryFieldSelected( - index, - typeof field === 'string' ? field : field.field - ); - return { - label: typeof field === 'string' ? field : field.field, - prepend: ( - {}} - /> - ), - checked: checked ? 'on' : undefined, - 'data-test-subj': 'queryField', - }; - })} - listProps={{ - bordered: false, - showIcons: false, - }} - onChange={(newOptions) => updateFields(index, newOptions)} - > - {(list) => list} - - {group.skipped_fields > 0 && ( - <> - - - - - - {` `} - - - - - - - - - - - )} - - - - ))} - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/search_playground/public/embeddable.tsx b/x-pack/plugins/search_playground/public/embeddable.tsx index f21c0c0f667eb..10eaec93fab8a 100644 --- a/x-pack/plugins/search_playground/public/embeddable.tsx +++ b/x-pack/plugins/search_playground/public/embeddable.tsx @@ -9,16 +9,15 @@ import React from 'react'; import { dynamic } from '@kbn/shared-ux-utility'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { CoreStart } from '@kbn/core-lifecycle-browser'; +import { QueryClientProvider } from '@tanstack/react-query'; import { AppPluginStartDependencies } from './types'; +import { queryClient } from './utils/query_client'; +import { AppProps } from './components/app'; -export const Playground = dynamic(async () => ({ +export const Playground = dynamic>(async () => ({ default: (await import('./components/app')).App, })); -export const PlaygroundToolbar = dynamic(async () => ({ - default: (await import('./components/toolbar')).Toolbar, -})); - export const PlaygroundProvider = dynamic(async () => ({ default: (await import('./providers/playground_provider')).PlaygroundProvider, })); @@ -32,6 +31,8 @@ export const getPlaygroundProvider = (props: React.ComponentProps) => ( - + + + ); diff --git a/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts b/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts index f78ea3accf206..e2ca982f2dc3f 100644 --- a/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts +++ b/x-pack/plugins/search_playground/public/hooks/use_indices_fields.ts @@ -9,13 +9,15 @@ import { useQuery } from '@tanstack/react-query'; import { useKibana } from './use_kibana'; import { APIRoutes, IndicesQuerySourceFields } from '../types'; +const initialData = {}; + export const useIndicesFields = (indices: string[] = []) => { const { services } = useKibana(); - const { data, isLoading } = useQuery({ + const { data, isLoading, isFetching } = useQuery({ enabled: indices.length > 0, queryKey: ['fields', indices.toString()], - initialData: {}, + initialData, queryFn: async () => { const response = await services.http.post( APIRoutes.POST_QUERY_SOURCE_FIELDS, @@ -30,5 +32,5 @@ export const useIndicesFields = (indices: string[] = []) => { }, }); - return { fields: data!, isLoading }; + return { fields: data, isLoading: isLoading || isFetching }; }; diff --git a/x-pack/plugins/search_playground/public/hooks/use_query_indices.ts b/x-pack/plugins/search_playground/public/hooks/use_query_indices.ts index 6740928ac903c..d011b471a68d6 100644 --- a/x-pack/plugins/search_playground/public/hooks/use_query_indices.ts +++ b/x-pack/plugins/search_playground/public/hooks/use_query_indices.ts @@ -29,7 +29,8 @@ export const useQueryIndices = ( return response.indices; }, + initialData: [], }); - return { indices: data || [], isLoading }; + return { indices: data, isLoading }; }; diff --git a/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts b/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts index bc9a37060fb6f..2fe9fbc237ff3 100644 --- a/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts +++ b/x-pack/plugins/search_playground/public/hooks/use_source_indices_field.ts @@ -5,12 +5,11 @@ * 2.0. */ -import { useQuery } from '@tanstack/react-query'; -import { useController, useFormContext } from 'react-hook-form'; +import { useController } from 'react-hook-form'; import { IndexName } from '@elastic/elasticsearch/lib/api/types'; -import { useEffect, useState } from 'react'; -import { useKibana } from './use_kibana'; -import { APIRoutes, IndicesQuerySourceFields } from '../types'; +import { useCallback, useEffect, useState } from 'react'; +import { merge } from 'lodash'; +import { useIndicesFields } from './use_indices_fields'; import { ChatForm, ChatFormFields } from '../types'; import { createQuery, @@ -36,10 +35,7 @@ export const getIndicesWithNoSourceFields = ( export const useSourceIndicesFields = () => { const usageTracker = useUsageTracker(); - const { services } = useKibana(); const [loading, setLoading] = useState(false); - const [noFieldsIndicesWarning, setNoFieldsIndicesWarning] = useState(null); - const { resetField } = useFormContext(); const { field: { value: selectedIndices, onChange: onIndicesChange }, @@ -55,73 +51,74 @@ export const useSourceIndicesFields = () => { }); const { - field: { onChange: onSourceFieldsChange }, - } = useController({ - name: ChatFormFields.sourceFields, + field: { onChange: onQueryFieldsOnChange, value: queryFields }, + } = useController({ + name: ChatFormFields.queryFields, }); - const { data: fields } = useQuery({ - enabled: selectedIndices.length > 0, - queryKey: ['fields', selectedIndices.toString()], - queryFn: async () => { - const response = await services.http.post( - APIRoutes.POST_QUERY_SOURCE_FIELDS, - { - body: JSON.stringify({ indices: selectedIndices }), - } - ); - return response; - }, + const { + field: { onChange: onSourceFieldsChange, value: sourceFields }, + } = useController({ + name: ChatFormFields.sourceFields, }); + const { fields, isLoading: isFieldsLoading } = useIndicesFields(selectedIndices); useEffect(() => { if (fields) { - resetField(ChatFormFields.queryFields); - const defaultFields = getDefaultQueryFields(fields); const defaultSourceFields = getDefaultSourceFields(fields); + const mergedQueryFields = merge(defaultFields, queryFields); + const mergedSourceFields = merge(defaultSourceFields, sourceFields); - const indicesWithNoSourceFields = getIndicesWithNoSourceFields(defaultSourceFields); + onElasticsearchQueryChange(createQuery(mergedQueryFields, mergedSourceFields, fields)); + onQueryFieldsOnChange(mergedQueryFields); - if (indicesWithNoSourceFields) { - setNoFieldsIndicesWarning(indicesWithNoSourceFields); - } else { - setNoFieldsIndicesWarning(null); - } - - onElasticsearchQueryChange(createQuery(defaultFields, defaultSourceFields, fields)); - onSourceFieldsChange(defaultSourceFields); + onSourceFieldsChange(mergedSourceFields); usageTracker?.count( AnalyticsEvents.sourceFieldsLoaded, Object.values(fields)?.flat()?.length ); - } else { - setNoFieldsIndicesWarning(null); } setLoading(false); // eslint-disable-next-line react-hooks/exhaustive-deps }, [fields]); - const addIndex = (newIndex: IndexName) => { - const newIndices = [...selectedIndices, newIndex]; - setLoading(true); - onIndicesChange(newIndices); - usageTracker?.count(AnalyticsEvents.sourceIndexUpdated, newIndices.length); - }; - - const removeIndex = (index: IndexName) => { - const newIndices = selectedIndices.filter((indexName: string) => indexName !== index); - setLoading(true); - onIndicesChange(newIndices); - usageTracker?.count(AnalyticsEvents.sourceIndexUpdated, newIndices.length); - }; + const addIndex = useCallback( + (newIndex: IndexName) => { + const newIndices = [...selectedIndices, newIndex]; + setLoading(true); + onIndicesChange(newIndices); + usageTracker?.count(AnalyticsEvents.sourceIndexUpdated, newIndices.length); + }, + [onIndicesChange, selectedIndices, usageTracker] + ); + + const removeIndex = useCallback( + (index: IndexName) => { + const newIndices = selectedIndices.filter((indexName: string) => indexName !== index); + setLoading(true); + onIndicesChange(newIndices); + usageTracker?.count(AnalyticsEvents.sourceIndexUpdated, newIndices.length); + }, + [onIndicesChange, selectedIndices, usageTracker] + ); + + const setIndices = useCallback( + (indices: IndexName[]) => { + setLoading(true); + onIndicesChange(indices); + usageTracker?.count(AnalyticsEvents.sourceIndexUpdated, indices.length); + }, + [onIndicesChange, usageTracker] + ); return { indices: selectedIndices, fields, loading, + isFieldsLoading, addIndex, removeIndex, - noFieldsIndicesWarning, + setIndices, }; }; diff --git a/x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx b/x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx index 7dd1a43d6fc01..431eab3cd943c 100644 --- a/x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx +++ b/x-pack/plugins/search_playground/public/hooks/use_source_indices_fields.test.tsx @@ -165,7 +165,6 @@ describe.skip('useSourceIndicesFields Hook', () => { expect(postMock).toHaveBeenCalled(); await act(async () => { - expect(result.current.noFieldsIndicesWarning).toEqual('missing_fields_index'); expect(result.current.loading).toBe(false); expect(getValues()).toMatchInlineSnapshot(` Object { @@ -219,7 +218,6 @@ describe.skip('useSourceIndicesFields Hook', () => { expect(postMock).toHaveBeenCalled(); await act(async () => { - expect(result.current.noFieldsIndicesWarning).toBeNull(); expect(result.current.loading).toBe(false); expect(getValues()).toMatchInlineSnapshot(` Object { diff --git a/x-pack/plugins/search_playground/public/plugin.ts b/x-pack/plugins/search_playground/public/plugin.ts index bb8403366a6ed..8b2976cab10c9 100644 --- a/x-pack/plugins/search_playground/public/plugin.ts +++ b/x-pack/plugins/search_playground/public/plugin.ts @@ -15,7 +15,7 @@ import { import { PLUGIN_ID, PLUGIN_NAME } from '../common'; import { docLinks } from '../common/doc_links'; import { PlaygroundHeaderDocs } from './components/playground_header_docs'; -import { PlaygroundToolbar, Playground, getPlaygroundProvider } from './embeddable'; +import { Playground, getPlaygroundProvider } from './embeddable'; import { AppPluginStartDependencies, SearchPlaygroundConfigType, @@ -60,7 +60,6 @@ export class SearchPlaygroundPlugin docLinks.setDocLinks(core.docLinks.links); return { PlaygroundProvider: getPlaygroundProvider(core, deps), - PlaygroundToolbar, Playground, PlaygroundHeaderDocs, }; diff --git a/x-pack/plugins/search_playground/public/providers/playground_provider.tsx b/x-pack/plugins/search_playground/public/providers/playground_provider.tsx index 562658b4b8bd5..6700bb4c66a57 100644 --- a/x-pack/plugins/search_playground/public/providers/playground_provider.tsx +++ b/x-pack/plugins/search_playground/public/providers/playground_provider.tsx @@ -5,32 +5,30 @@ * 2.0. */ -import React, { FC, PropsWithChildren } from 'react'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import React, { FC, useEffect } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; -import { ChatForm } from '../types'; +import { useLLMsModels } from '../hooks/use_llms_models'; +import { ChatForm, ChatFormFields } from '../types'; -const queryClient = new QueryClient({}); - -export interface PlaygroundProviderProps { - children: React.ReactNode; -} - -export const PlaygroundProvider: FC> = ({ - children, -}) => { +export const PlaygroundProvider: FC = ({ children }) => { + const models = useLLMsModels(); const form = useForm({ defaultValues: { prompt: 'You are an assistant for question-answering tasks.', doc_size: 3, source_fields: {}, indices: [], + summarization_model: {}, }, }); - return ( - - {children} - - ); + useEffect(() => { + const defaultModel = models.find((model) => !model.disabled); + + if (defaultModel) { + form.setValue(ChatFormFields.summarizationModel, defaultModel); + } + }, [form, models]); + + return {children}; }; diff --git a/x-pack/plugins/search_playground/public/types.ts b/x-pack/plugins/search_playground/public/types.ts index eea41aae96d59..2076ac2190b99 100644 --- a/x-pack/plugins/search_playground/public/types.ts +++ b/x-pack/plugins/search_playground/public/types.ts @@ -25,7 +25,6 @@ import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import { ChatRequestData } from '../common/types'; import type { App } from './components/app'; import type { PlaygroundProvider as PlaygroundProviderComponent } from './providers/playground_provider'; -import type { Toolbar } from './components/toolbar'; import { PlaygroundHeaderDocs } from './components/playground_header_docs'; export * from '../common/types'; @@ -34,7 +33,6 @@ export * from '../common/types'; export interface SearchPlaygroundPluginSetup {} export interface SearchPlaygroundPluginStart { PlaygroundProvider: React.FC>; - PlaygroundToolbar: React.FC>; Playground: React.FC>; PlaygroundHeaderDocs: React.FC>; } diff --git a/x-pack/plugins/search_playground/public/utils/create_query.ts b/x-pack/plugins/search_playground/public/utils/create_query.ts index fadf15f291abd..1f3fee0e35104 100644 --- a/x-pack/plugins/search_playground/public/utils/create_query.ts +++ b/x-pack/plugins/search_playground/public/utils/create_query.ts @@ -55,6 +55,9 @@ export function createQuery( const indices = Object.keys(fieldDescriptors); const boolMatches = Object.keys(fields).reduce( (acc, index) => { + if (!fieldDescriptors[index]) { + return acc; + } const indexFields: string[] = fields[index]; const indexFieldDescriptors: QuerySourceFields = fieldDescriptors[index]; @@ -320,6 +323,21 @@ export function getDefaultSourceFields(fieldDescriptors: IndicesQuerySourceField return indexFields; } +export const getIndicesWithNoSourceFields = ( + fields: IndicesQuerySourceFields +): string | undefined => { + const defaultSourceFields = getDefaultSourceFields(fields); + const indices = Object.keys(defaultSourceFields).reduce((result, index: string) => { + if (defaultSourceFields[index].length === 0) { + result.push(index); + } + + return result; + }, []); + + return indices.length === 0 ? undefined : indices.join(); +}; + export function getDefaultQueryFields(fieldDescriptors: IndicesQuerySourceFields): IndexFields { const indexFields = Object.keys(fieldDescriptors).reduce( (acc: IndexFields, index: string) => { diff --git a/x-pack/plugins/search_playground/public/utils/query_client.ts b/x-pack/plugins/search_playground/public/utils/query_client.ts new file mode 100644 index 0000000000000..c5d9874fe71ef --- /dev/null +++ b/x-pack/plugins/search_playground/public/utils/query_client.ts @@ -0,0 +1,10 @@ +/* + * 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 { QueryClient } from '@tanstack/react-query'; + +export const queryClient = new QueryClient({}); diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts index 7c63ace9fc706..198bc004ecb60 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts @@ -30,6 +30,7 @@ describe('API Keys', () => { >; let mockLicense: jest.Mocked; let logger: Logger; + const roleDescriptors: { [key: string]: any } = { foo: true }; beforeEach(() => { mockValidateKibanaPrivileges.mockReset().mockReturnValue({ validationErrors: [] }); @@ -239,9 +240,10 @@ describe('API Keys', () => { }); const result = await apiKeys.create(httpServerMock.createKibanaRequest(), { name: 'key-name', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, expiration: '1d', }); + expect(result).toEqual({ api_key: 'abc123', expiration: '1d', @@ -253,7 +255,7 @@ describe('API Keys', () => { expect(mockScopedClusterClient.asCurrentUser.security.createApiKey).toHaveBeenCalledWith({ body: { name: 'key-name', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, expiration: '1d', }, }); @@ -343,7 +345,7 @@ describe('API Keys', () => { const result = await apiKeys.update(httpServerMock.createKibanaRequest(), { id: 'test_id', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, metadata: {}, }); @@ -370,7 +372,7 @@ describe('API Keys', () => { const result = await apiKeys.update(httpServerMock.createKibanaRequest(), { id: 'test_id', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, metadata: {}, }); @@ -473,7 +475,7 @@ describe('API Keys', () => { }), { name: 'test_api_key', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, expiration: '1d', } ); @@ -512,7 +514,7 @@ describe('API Keys', () => { }), { name: 'test_api_key', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, expiration: '1d', } ); @@ -527,7 +529,7 @@ describe('API Keys', () => { body: { api_key: { name: 'test_api_key', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, expiration: '1d', }, grant_type: 'access_token', @@ -553,7 +555,7 @@ describe('API Keys', () => { }), { name: 'test_api_key', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, expiration: '1d', } ); @@ -592,7 +594,7 @@ describe('API Keys', () => { }), { name: 'test_api_key', - role_descriptors: { foo: true }, + role_descriptors: roleDescriptors, expiration: '1d', } ) diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts index 1fbd99b4dd812..054ab59fbd0bb 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts @@ -20,6 +20,7 @@ import type { InvalidateAPIKeysParams, ValidateAPIKeyParams, } from '@kbn/security-plugin-types-server'; +import { isCreateRestAPIKeyParams } from '@kbn/security-plugin-types-server'; import { getFakeKibanaRequest } from './fake_kibana_request'; import type { SecurityLicense } from '../../../common'; @@ -96,7 +97,6 @@ export class APIKeys implements APIKeysType { this.logger.debug( `Testing if API Keys are enabled by attempting to invalidate a non-existant key: ${id}` ); - try { await this.clusterClient.asInternalUser.security.invalidateApiKey({ body: { @@ -125,7 +125,6 @@ export class APIKeys implements APIKeysType { this.logger.debug( `Testing if cross-cluster API Keys are enabled by attempting to update a non-existant key: ${id}` ); - try { await this.clusterClient.asInternalUser.transport.request({ method: 'PUT', @@ -155,13 +154,13 @@ export class APIKeys implements APIKeysType { if (!this.license.isEnabled()) { return null; } - const { type, expiration, name, metadata } = createParams; const scopedClusterClient = this.clusterClient.asScoped(request); this.logger.debug('Trying to create an API key'); let result: CreateAPIKeyResult; + try { if (type === 'cross_cluster') { result = await scopedClusterClient.asCurrentUser.transport.request({ @@ -175,13 +174,13 @@ export class APIKeys implements APIKeysType { name, expiration, metadata, - role_descriptors: - 'role_descriptors' in createParams - ? createParams.role_descriptors - : this.parseRoleDescriptorsWithKibanaPrivileges( - createParams.kibana_role_descriptors, - false - ), + role_descriptors: isCreateRestAPIKeyParams(createParams) + ? createParams.role_descriptors + : this.parseRoleDescriptorsWithKibanaPrivileges( + createParams.kibana_role_descriptors, + this.kibanaFeatures, + false + ), }, }); } @@ -234,6 +233,7 @@ export class APIKeys implements APIKeysType { ? updateParams.role_descriptors : this.parseRoleDescriptorsWithKibanaPrivileges( updateParams.kibana_role_descriptors, + this.kibanaFeatures, true ), }); @@ -279,12 +279,12 @@ export class APIKeys implements APIKeysType { ); const { expiration, metadata, name } = createParams; - const roleDescriptors = 'role_descriptors' in createParams ? createParams.role_descriptors : this.parseRoleDescriptorsWithKibanaPrivileges( createParams.kibana_role_descriptors, + this.kibanaFeatures, false ); @@ -293,7 +293,6 @@ export class APIKeys implements APIKeysType { authorizationHeader, clientAuthorizationHeader ); - // User needs `manage_api_key` or `grant_api_key` privilege to use this API let result: GrantAPIKeyResult; try { @@ -318,7 +317,6 @@ export class APIKeys implements APIKeysType { } this.logger.debug(`Trying to invalidate ${params.ids.length} an API key as current user`); - let result: InvalidateAPIKeyResult; try { // User needs `manage_api_key` privilege to use this API @@ -354,6 +352,7 @@ export class APIKeys implements APIKeysType { this.logger.debug(`Trying to invalidate ${params.ids.length} API keys`); let result: InvalidateAPIKeyResult; + try { // Internal user needs `cluster:admin/xpack/security/api_key/invalidate` privilege to use this API result = await this.clusterClient.asInternalUser.security.invalidateApiKey({ @@ -384,7 +383,6 @@ export class APIKeys implements APIKeysType { const fakeRequest = getFakeKibanaRequest(apiKeyPrams); this.logger.debug(`Trying to validate an API key`); - try { await this.clusterClient.asScoped(fakeRequest).asCurrentUser.security.authenticate(); this.logger.debug(`API key was validated successfully`); @@ -445,6 +443,7 @@ export class APIKeys implements APIKeysType { private parseRoleDescriptorsWithKibanaPrivileges( kibanaRoleDescriptors: CreateRestAPIKeyWithKibanaPrivilegesParams['kibana_role_descriptors'], + features: KibanaFeature[], isEdit: boolean ) { const roleDescriptors = Object.create(null); @@ -452,10 +451,7 @@ export class APIKeys implements APIKeysType { const allValidationErrors: string[] = []; if (kibanaRoleDescriptors) { Object.entries(kibanaRoleDescriptors).forEach(([roleKey, roleDescriptor]) => { - const { validationErrors } = validateKibanaPrivileges( - this.kibanaFeatures, - roleDescriptor.kibana - ); + const { validationErrors } = validateKibanaPrivileges(features, roleDescriptor.kibana); allValidationErrors.push(...validationErrors); const applications = transformPrivilegesToElasticsearchPrivileges( diff --git a/x-pack/plugins/security/server/authentication/authentication_service.mock.ts b/x-pack/plugins/security/server/authentication/authentication_service.mock.ts index de87e7161bda8..676b039eefaa4 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.mock.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.mock.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { apiKeysMock } from '@kbn/core-security-server-mocks'; import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; -import { apiKeysMock } from './api_keys/api_keys.mock'; import type { InternalAuthenticationServiceStart } from './authentication_service'; export const authenticationServiceMock = { diff --git a/x-pack/plugins/security/server/authentication/authentication_service.ts b/x-pack/plugins/security/server/authentication/authentication_service.ts index abc400c0b6305..cf99084d4d0bc 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.ts @@ -348,7 +348,6 @@ export class AuthenticationService { applicationName, kibanaFeatures, }); - /** * Retrieves server protocol name/host name/port and merges it with `xpack.security.public` config * to construct a server base URL (deprecated, used by the SAML provider only). diff --git a/x-pack/plugins/security/server/build_delegate_apis.test.ts b/x-pack/plugins/security/server/build_delegate_apis.test.ts index 59963ad2aef8e..297ee6ebf92c4 100644 --- a/x-pack/plugins/security/server/build_delegate_apis.test.ts +++ b/x-pack/plugins/security/server/build_delegate_apis.test.ts @@ -65,6 +65,21 @@ describe('buildSecurityApi', () => { expect(auditService.asScoped(request).log).toHaveBeenCalledWith({ message: 'an event' }); }); }); + + describe('authc.apiKeys', () => { + it('properly delegates to the service', async () => { + await authc.apiKeys.areAPIKeysEnabled(); + expect(authc.apiKeys.areAPIKeysEnabled).toHaveBeenCalledTimes(1); + }); + + it('returns the result from the service', async () => { + authc.apiKeys.areAPIKeysEnabled.mockReturnValue(Promise.resolve(false)); + + const areAPIKeysEnabled = await authc.apiKeys.areAPIKeysEnabled(); + + expect(areAPIKeysEnabled).toBe(false); + }); + }); }); describe('buildUserProfileApi', () => { diff --git a/x-pack/plugins/security/server/build_delegate_apis.ts b/x-pack/plugins/security/server/build_delegate_apis.ts index fb782f3db256f..f6d57cfc8a4a8 100644 --- a/x-pack/plugins/security/server/build_delegate_apis.ts +++ b/x-pack/plugins/security/server/build_delegate_apis.ts @@ -24,6 +24,17 @@ export const buildSecurityApi = ({ getCurrentUser: (request) => { return getAuthc().getCurrentUser(request); }, + apiKeys: { + areAPIKeysEnabled: () => getAuthc().apiKeys.areAPIKeysEnabled(), + areCrossClusterAPIKeysEnabled: () => getAuthc().apiKeys.areAPIKeysEnabled(), + grantAsInternalUser: (request, createParams) => + getAuthc().apiKeys.grantAsInternalUser(request, createParams), + create: (request, createParams) => getAuthc().apiKeys.create(request, createParams), + update: (request, updateParams) => getAuthc().apiKeys.update(request, updateParams), + validate: (apiKeyParams) => getAuthc().apiKeys.validate(apiKeyParams), + invalidate: (request, params) => getAuthc().apiKeys.invalidate(request, params), + invalidateAsInternalUser: (params) => getAuthc().apiKeys.invalidateAsInternalUser(params), + }, }, audit: { asScoped(request) { diff --git a/x-pack/plugins/security/server/mocks.ts b/x-pack/plugins/security/server/mocks.ts index a5473176fc7e7..e47faeba525a0 100644 --- a/x-pack/plugins/security/server/mocks.ts +++ b/x-pack/plugins/security/server/mocks.ts @@ -7,7 +7,7 @@ import type { TransportResult } from '@elastic/elasticsearch'; -import { securityServiceMock } from '@kbn/core-security-server-mocks'; +import { apiKeysMock, securityServiceMock } from '@kbn/core-security-server-mocks'; import { auditServiceMock } from './audit/mocks'; import { authenticationServiceMock } from './authentication/authentication_service.mock'; @@ -19,7 +19,10 @@ function createSetupMock() { const mockAuthz = authorizationMock.create(); return { audit: auditServiceMock.create(), - authc: { getCurrentUser: jest.fn() }, + authc: { + getCurrentUser: jest.fn(), + apiKeys: apiKeysMock.create(), + }, authz: { actions: mockAuthz.actions, checkPrivilegesWithRequest: mockAuthz.checkPrivilegesWithRequest, diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 9d5ffde67b1d7..a500454f98fa9 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -77,7 +77,9 @@ export interface SecurityPluginSetup extends SecurityPluginSetupWithoutDeprecate /** * @deprecated Use `authc` methods from the `SecurityServiceStart` contract instead. */ - authc: { getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null }; + authc: { + getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; + }; /** * @deprecated Use `authz` methods from the `SecurityServiceStart` contract instead. */ @@ -110,6 +112,7 @@ export class SecurityPlugin private readonly logger: Logger; private authorizationSetup?: AuthorizationServiceSetupInternal; private auditSetup?: AuditServiceSetup; + private configSubscription?: Subscription; private config?: ConfigType; @@ -189,6 +192,7 @@ export class SecurityPlugin this.initializerContext.logger.get('authentication') ); this.auditService = new AuditService(this.initializerContext.logger.get('audit')); + this.elasticsearchService = new ElasticsearchService( this.initializerContext.logger.get('elasticsearch') ); @@ -340,7 +344,9 @@ export class SecurityPlugin return Object.freeze({ audit: this.auditSetup, - authc: { getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request) }, + authc: { + getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request), + }, authz: { actions: this.authorizationSetup.actions, checkPrivilegesWithRequest: this.authorizationSetup.checkPrivilegesWithRequest, @@ -421,8 +427,8 @@ export class SecurityPlugin return Object.freeze({ authc: { - apiKeys: this.authenticationStart.apiKeys, getCurrentUser: this.authenticationStart.getCurrentUser, + apiKeys: this.authenticationStart.apiKeys, }, authz: { actions: this.authorizationSetup!.actions, diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index 64d162839cf1e..10c1ada6ede15 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -83,7 +83,7 @@ "@kbn/core-user-profile-browser", "@kbn/security-api-key-management", "@kbn/security-form-components", - "@kbn/core-security-server-mocks", + "@kbn/core-security-server-mocks" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts index df0084d6ff0e3..dadb6bfa4165d 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts @@ -304,8 +304,29 @@ export type TimestampOverrideFallbackDisabled = z.infer; export const RequiredField = z.object({ /** @@ -368,6 +389,39 @@ export const SavedObjectResolveAliasPurpose = z.enum([ export type SavedObjectResolveAliasPurposeEnum = typeof SavedObjectResolveAliasPurpose.enum; export const SavedObjectResolveAliasPurposeEnum = SavedObjectResolveAliasPurpose.enum; +/** + * Related integration is a potential dependency of a rule. It's assumed that if the user installs +one of the related integrations of a rule, the rule might start to work properly because it will +have source events (generated by this integration) potentially matching the rule's query. + +NOTE: Proper work is not guaranteed, because a related integration, if installed, can be +configured differently or generate data that is not necessarily relevant for this rule. + +Related integration is a combination of a Fleet package and (optionally) one of the +package's "integrations" that this package contains. It is represented by 3 properties: + +- `package`: name of the package (required, unique id) +- `version`: version of the package (required, semver-compatible) +- `integration`: name of the integration of this package (optional, id within the package) + +There are Fleet packages like `windows` that contain only one integration; in this case, +`integration` should be unspecified. There are also packages like `aws` and `azure` that contain +several integrations; in this case, `integration` should be specified. + +@example +const x: RelatedIntegration = { + package: 'windows', + version: '1.5.x', +}; + +@example +const x: RelatedIntegration = { + package: 'azure', + version: '~1.1.6', + integration: 'activitylogs', +}; + + */ export type RelatedIntegration = z.infer; export const RelatedIntegration = z.object({ package: NonEmptyString, @@ -378,6 +432,22 @@ export const RelatedIntegration = z.object({ export type RelatedIntegrationArray = z.infer; export const RelatedIntegrationArray = z.array(RelatedIntegration); +/** + * Schema for fields relating to investigation fields. These are user defined fields we use to highlight +in various features in the UI such as alert details flyout and exceptions auto-population from alert. +Added in PR #163235 +Right now we only have a single field but anticipate adding more related fields to store various +configuration states such as `override` - where a user might say if they want only these fields to +display, or if they want these fields + the fields we select. When expanding this field, it may look +something like: +```typescript +const investigationFields = z.object({ + field_names: NonEmptyArray(NonEmptyString), + override: z.boolean().optional(), +}); +``` + + */ export type InvestigationFields = z.infer; export const InvestigationFields = z.object({ field_names: z.array(NonEmptyString).min(1), diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml index cd5e238723f6a..b2d72a561e46c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml @@ -315,7 +315,28 @@ components: RequiredField: type: object - description: Describes an Elasticsearch field that is needed for the rule to function + description: | + Describes an Elasticsearch field that is needed for the rule to function. + + Almost all types of Security rules check source event documents for a match to some kind of + query or filter. If a document has certain field with certain values, then it's a match and + the rule will generate an alert. + + Required field is an event field that must be present in the source indices of a given rule. + + @example + const standardEcsField: RequiredField = { + name: 'event.action', + type: 'keyword', + ecs: true, + }; + + @example + const nonEcsField: RequiredField = { + name: 'winlog.event_data.AttributeLDAPDisplayName', + type: 'keyword', + ecs: false, + }; properties: name: $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' @@ -376,6 +397,37 @@ components: RelatedIntegration: type: object + description: | + Related integration is a potential dependency of a rule. It's assumed that if the user installs + one of the related integrations of a rule, the rule might start to work properly because it will + have source events (generated by this integration) potentially matching the rule's query. + + NOTE: Proper work is not guaranteed, because a related integration, if installed, can be + configured differently or generate data that is not necessarily relevant for this rule. + + Related integration is a combination of a Fleet package and (optionally) one of the + package's "integrations" that this package contains. It is represented by 3 properties: + + - `package`: name of the package (required, unique id) + - `version`: version of the package (required, semver-compatible) + - `integration`: name of the integration of this package (optional, id within the package) + + There are Fleet packages like `windows` that contain only one integration; in this case, + `integration` should be unspecified. There are also packages like `aws` and `azure` that contain + several integrations; in this case, `integration` should be specified. + + @example + const x: RelatedIntegration = { + package: 'windows', + version: '1.5.x', + }; + + @example + const x: RelatedIntegration = { + package: 'azure', + version: '~1.1.6', + integration: 'activitylogs', + }; properties: package: $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' @@ -392,10 +444,22 @@ components: items: $ref: '#/components/schemas/RelatedIntegration' - # Schema for fields relating to investigation fields, these are user defined fields we use to highlight in various features in the UI such as alert details flyout and exceptions auto-population from alert. Added in PR #163235 - # Right now we only have a single field but anticipate adding more related fields to store various configuration states such as `override` - where a user might say if they want only these fields to display, or if they want these fields + the fields we select. InvestigationFields: type: object + description: | + Schema for fields relating to investigation fields. These are user defined fields we use to highlight + in various features in the UI such as alert details flyout and exceptions auto-population from alert. + Added in PR #163235 + Right now we only have a single field but anticipate adding more related fields to store various + configuration states such as `override` - where a user might say if they want only these fields to + display, or if they want these fields + the fields we select. When expanding this field, it may look + something like: + ```typescript + const investigationFields = z.object({ + field_names: NonEmptyArray(NonEmptyString), + override: z.boolean().optional(), + }); + ``` properties: field_names: type: array diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.mock.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.mock.ts index 1a39d65c1c22f..c605436576995 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.mock.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.mock.ts @@ -6,16 +6,18 @@ */ import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../constants'; +import { getListArrayMock } from '../../../../detection_engine/schemas/types/lists.mock'; import type { EqlRule, EsqlRule, MachineLearningRule, + NewTermsRule, QueryRule, SavedQueryRule, SharedResponseProps, ThreatMatchRule, + ThresholdRule, } from './rule_schemas.gen'; -import { getListArrayMock } from '../../../../detection_engine/schemas/types/lists.mock'; export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; @@ -238,3 +240,27 @@ export const getRulesEqlSchemaMock = (anchorDate: string = ANCHOR_DATE): EqlRule tiebreaker_field: undefined, }; }; + +export const getRulesNewTermsSchemaMock = (anchorDate: string = ANCHOR_DATE): NewTermsRule => { + return { + ...getResponseBaseParams(anchorDate), + type: 'new_terms', + query: '*', + language: 'kuery', + new_terms_fields: ['user.name'], + history_window_start: 'now-7d', + }; +}; + +export const getRulesThresholdSchemaMock = (anchorDate: string = ANCHOR_DATE): ThresholdRule => { + return { + ...getResponseBaseParams(anchorDate), + type: 'threshold', + language: 'kuery', + query: 'user.name: root or user.name: admin', + threshold: { + field: 'some.field', + value: 4, + }, + }; +}; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/time_duration.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/time_duration.test.ts new file mode 100644 index 0000000000000..b455894333aee --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/time_duration.test.ts @@ -0,0 +1,135 @@ +/* + * 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 { expectParseError, expectParseSuccess, stringifyZodError } from '@kbn/zod-helpers'; +import { TimeDuration } from './time_duration'; // Update with the actual path to your TimeDuration file + +describe('TimeDuration schema', () => { + test('it should validate a correctly formed TimeDuration with time unit of seconds', () => { + const payload = '1s'; + const schema = TimeDuration({ allowedUnits: ['s'] }); + + const result = schema.safeParse(payload); + expectParseSuccess(result); + expect(result.data).toEqual(payload); + }); + + test('it should validate a correctly formed TimeDuration with time unit of minutes', () => { + const payload = '100m'; + const schema = TimeDuration({ allowedUnits: ['s', 'm'] }); + + const result = schema.safeParse(payload); + expectParseSuccess(result); + expect(result.data).toEqual(payload); + }); + + test('it should validate a correctly formed TimeDuration with time unit of hours', () => { + const payload = '10000000h'; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }); + + const result = schema.safeParse(payload); + expectParseSuccess(result); + expect(result.data).toEqual(payload); + }); + + test('it should validate a correctly formed TimeDuration with time unit of days', () => { + const payload = '7d'; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }); + + const result = schema.safeParse(payload); + expectParseSuccess(result); + expect(result.data).toEqual(payload); + }); + + test('it should NOT validate a correctly formed TimeDuration with time unit of seconds if it is not an allowed unit', () => { + const payload = '30s'; + const schema = TimeDuration({ allowedUnits: ['m', 'h', 'd'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. \\"30s\\", \\"1m\\", \\"2h\\", \\"7d\\""` + ); + }); + + test('it should NOT validate a negative TimeDuration', () => { + const payload = '-10s'; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. \\"30s\\", \\"1m\\", \\"2h\\", \\"7d\\""` + ); + }); + + test('it should NOT validate a fractional number', () => { + const payload = '1.5s'; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. \\"30s\\", \\"1m\\", \\"2h\\", \\"7d\\""` + ); + }); + + test('it should NOT validate a TimeDuration with an invalid time unit', () => { + const payload = '10000000days'; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. \\"30s\\", \\"1m\\", \\"2h\\", \\"7d\\""` + ); + }); + + test('it should NOT validate a TimeDuration with a time interval with incorrect format', () => { + const payload = '100ff0000w'; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. \\"30s\\", \\"1m\\", \\"2h\\", \\"7d\\""` + ); + }); + + test('it should NOT validate an empty string', () => { + const payload = ''; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. \\"30s\\", \\"1m\\", \\"2h\\", \\"7d\\""` + ); + }); + + test('it should NOT validate a number', () => { + const payload = 100; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Expected string, received number"` + ); + }); + + test('it should NOT validate a TimeDuration with a valid time unit but unsafe integer', () => { + const payload = `${Math.pow(2, 53)}h`; + const schema = TimeDuration({ allowedUnits: ['s', 'm', 'h'] }); + + const result = schema.safeParse(payload); + expectParseError(result); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot( + `"Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. \\"30s\\", \\"1m\\", \\"2h\\", \\"7d\\""` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/time_duration.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/time_duration.ts new file mode 100644 index 0000000000000..da5cea87f31cf --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/time_duration.ts @@ -0,0 +1,53 @@ +/* + * 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 { z } from 'zod'; + +type TimeUnits = 's' | 'm' | 'h' | 'd' | 'w' | 'y'; + +interface TimeDurationType { + allowedUnits: TimeUnits[]; +} + +const isTimeSafe = (time: number) => time >= 1 && Number.isSafeInteger(time); + +/** + * Types the TimeDuration as: + * - A string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time + * - in the format {safe_integer}{timeUnit}, e.g. "30s", "1m", "2h", "7d" + * + * Example usage: + * ``` + * const schedule: RuleSchedule = { + * interval: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }).parse('3h'), + * }; + * ``` + */ +export const TimeDuration = ({ allowedUnits }: TimeDurationType) => { + return z.string().refine( + (input) => { + if (input.trim() === '') return false; + + try { + const inputLength = input.length; + const time = Number(input.trim().substring(0, inputLength - 1)); + const unit = input.trim().at(-1) as TimeUnits; + + return isTimeSafe(time) && allowedUnits.includes(unit); + } catch (error) { + return false; + } + }, + { + message: + 'Invalid time duration format. Must be a string that is not empty, and composed of a positive integer greater than 0 followed by a unit of time in the format {safe_integer}{timeUnit}, e.g. "30s", "1m", "2h", "7d"', + } + ); +}; + +export type TimeDurationSchema = ReturnType; +export type TimeDuration = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/common_attributes.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/common_attributes.ts index 310f96b7bf946..ba07c49a7b130 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/common_attributes.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/common_attributes.ts @@ -6,9 +6,24 @@ */ import * as t from 'io-ts'; -import { listArray } from '@kbn/securitysolution-io-ts-list-types'; -import { NonEmptyString, version, UUID, NonEmptyArray } from '@kbn/securitysolution-io-ts-types'; -import { max_signals, threat } from '@kbn/securitysolution-io-ts-alerting-types'; +import { NonEmptyString, UUID } from '@kbn/securitysolution-io-ts-types'; + +/* +IMPORTANT NOTE ON THIS FILE: + +This file contains the remaining rule schema types created manually via io-ts. They have been +migrated to Zod schemas created via code generation out of OpenAPI schemas +(found in x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts) + +The remaining types here couldn't easily be deleted/replaced because they are dependencies in +complex derived schemas in two files: + +- x-pack/plugins/security_solution/common/api/detection_engine/rule_exceptions/find_exception_references/find_exception_references_route.ts +- x-pack/plugins/security_solution/common/api/timeline/model/api.ts + +Once those two files are migrated to Zod, the /common/api/detection_engine/model/rule_schema_legacy +folder can be removed. +*/ export type RuleObjectId = t.TypeOf; export const RuleObjectId = UUID; @@ -24,156 +39,6 @@ export const RuleSignatureId = t.string; // should be non-empty string? export type RuleName = t.TypeOf; export const RuleName = NonEmptyString; -export type RuleDescription = t.TypeOf; -export const RuleDescription = NonEmptyString; - -export type RuleVersion = t.TypeOf; -export const RuleVersion = version; - -export type IsRuleImmutable = t.TypeOf; -export const IsRuleImmutable = t.boolean; - -export type IsRuleEnabled = t.TypeOf; -export const IsRuleEnabled = t.boolean; - -export type RuleTagArray = t.TypeOf; -export const RuleTagArray = t.array(t.string); // should be non-empty strings? - -/** - * Note that this is a non-exact io-ts type as we allow extra meta information - * to be added to the meta object - */ -export type RuleMetadata = t.TypeOf; -export const RuleMetadata = t.UnknownRecord; // should be a more specific type? - -export type RuleLicense = t.TypeOf; -export const RuleLicense = t.string; - -export type RuleAuthorArray = t.TypeOf; -export const RuleAuthorArray = t.array(t.string); // should be non-empty strings? - -export type RuleFalsePositiveArray = t.TypeOf; -export const RuleFalsePositiveArray = t.array(t.string); // should be non-empty strings? - -export type RuleReferenceArray = t.TypeOf; -export const RuleReferenceArray = t.array(t.string); // should be non-empty strings? - -export type InvestigationGuide = t.TypeOf; -export const InvestigationGuide = t.string; - -/** - * Any instructions for the user for setting up their environment in order to start receiving - * source events for a given rule. - * - * It's a multiline text. Markdown is supported. - */ -export type SetupGuide = t.TypeOf; -export const SetupGuide = t.string; - -export type BuildingBlockType = t.TypeOf; -export const BuildingBlockType = t.string; - -export type AlertsIndex = t.TypeOf; -export const AlertsIndex = t.string; - -export type AlertsIndexNamespace = t.TypeOf; -export const AlertsIndexNamespace = t.string; - -export type ExceptionListArray = t.TypeOf; -export const ExceptionListArray = listArray; - -export type MaxSignals = t.TypeOf; -export const MaxSignals = max_signals; - -export type ThreatArray = t.TypeOf; -export const ThreatArray = t.array(threat); - -export type IndexPatternArray = t.TypeOf; -export const IndexPatternArray = t.array(t.string); - -export type DataViewId = t.TypeOf; -export const DataViewId = t.string; - -export type RuleQuery = t.TypeOf; -export const RuleQuery = t.string; - -/** - * TODO: Right now the filters is an "unknown", when it could more than likely - * become the actual ESFilter as a type. - */ -export type RuleFilterArray = t.TypeOf; // Filters are not easily type-able yet -export const RuleFilterArray = t.array(t.unknown); // Filters are not easily type-able yet - -export type RuleNameOverride = t.TypeOf; -export const RuleNameOverride = t.string; // should be non-empty string? - -export type TimestampOverride = t.TypeOf; -export const TimestampOverride = t.string; // should be non-empty string? - -export type TimestampOverrideFallbackDisabled = t.TypeOf; -export const TimestampOverrideFallbackDisabled = t.boolean; - -/** - * Almost all types of Security rules check source event documents for a match to some kind of - * query or filter. If a document has certain field with certain values, then it's a match and - * the rule will generate an alert. - * - * Required field is an event field that must be present in the source indices of a given rule. - * - * @example - * const standardEcsField: RequiredField = { - * name: 'event.action', - * type: 'keyword', - * ecs: true, - * }; - * - * @example - * const nonEcsField: RequiredField = { - * name: 'winlog.event_data.AttributeLDAPDisplayName', - * type: 'keyword', - * ecs: false, - * }; - */ -export type RequiredField = t.TypeOf; -export const RequiredField = t.exact( - t.type({ - name: NonEmptyString, - type: NonEmptyString, - ecs: t.boolean, - }) -); - -/** - * Array of event fields that must be present in the source indices of a given rule. - * - * @example - * const x: RequiredFieldArray = [ - * { - * name: 'event.action', - * type: 'keyword', - * ecs: true, - * }, - * { - * name: 'event.code', - * type: 'keyword', - * ecs: true, - * }, - * { - * name: 'winlog.event_data.AttributeLDAPDisplayName', - * type: 'keyword', - * ecs: false, - * }, - * ]; - */ -export type RequiredFieldArray = t.TypeOf; -export const RequiredFieldArray = t.array(RequiredField); - -export type TimelineTemplateId = t.TypeOf; -export const TimelineTemplateId = t.string; // should be non-empty string? - -export type TimelineTemplateTitle = t.TypeOf; -export const TimelineTemplateTitle = t.string; // should be non-empty string? - /** * Outcome is a property of the saved object resolve api * will tell us info about the rule after 8.0 migrations @@ -193,96 +58,3 @@ export const SavedObjectResolveAliasPurpose = t.union([ t.literal('savedObjectConversion'), t.literal('savedObjectImport'), ]); - -/** - * Related integration is a potential dependency of a rule. It's assumed that if the user installs - * one of the related integrations of a rule, the rule might start to work properly because it will - * have source events (generated by this integration) potentially matching the rule's query. - * - * NOTE: Proper work is not guaranteed, because a related integration, if installed, can be - * configured differently or generate data that is not necessarily relevant for this rule. - * - * Related integration is a combination of a Fleet package and (optionally) one of the - * package's "integrations" that this package contains. It is represented by 3 properties: - * - * - `package`: name of the package (required, unique id) - * - `version`: version of the package (required, semver-compatible) - * - `integration`: name of the integration of this package (optional, id within the package) - * - * There are Fleet packages like `windows` that contain only one integration; in this case, - * `integration` should be unspecified. There are also packages like `aws` and `azure` that contain - * several integrations; in this case, `integration` should be specified. - * - * @example - * const x: RelatedIntegration = { - * package: 'windows', - * version: '1.5.x', - * }; - * - * @example - * const x: RelatedIntegration = { - * package: 'azure', - * version: '~1.1.6', - * integration: 'activitylogs', - * }; - */ -export type RelatedIntegration = t.TypeOf; -export const RelatedIntegration = t.exact( - t.intersection([ - t.type({ - package: NonEmptyString, - version: NonEmptyString, - }), - t.partial({ - integration: NonEmptyString, - }), - ]) -); - -/** - * Array of related integrations. - * - * @example - * const x: RelatedIntegrationArray = [ - * { - * package: 'windows', - * version: '1.5.x', - * }, - * { - * package: 'azure', - * version: '~1.1.6', - * integration: 'activitylogs', - * }, - * ]; - */ -export type RelatedIntegrationArray = t.TypeOf; -export const RelatedIntegrationArray = t.array(RelatedIntegration); - -/** - * Schema for fields relating to investigation fields, these are user defined fields we use to highlight - * in various features in the UI such as alert details flyout and exceptions auto-population from alert. - * Added in PR #163235 - * Right now we only have a single field but anticipate adding more related fields to store various - * configuration states such as `override` - where a user might say if they want only these fields to - * display, or if they want these fields + the fields we select. When expanding this field, it may look - * something like: - * export const investigationFields = t.intersection([ - * t.exact( - * t.type({ - * field_names: NonEmptyArray(NonEmptyString), - * }) - * ), - * t.exact( - * t.partial({ - * overide: t.boolean, - * }) - * ), - * ]); - * - */ -export type InvestigationFields = t.TypeOf; -export const InvestigationFields = t.exact( - t.type({ - field_names: NonEmptyArray(NonEmptyString), - }) -); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/eql_attributes.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/eql_attributes.ts deleted file mode 100644 index 0bc029fa0d4a5..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/eql_attributes.ts +++ /dev/null @@ -1,19 +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 * as t from 'io-ts'; - -// Attributes specific to EQL rules - -export type EventCategoryOverride = t.TypeOf; -export const EventCategoryOverride = t.string; // should be non-empty string? - -export type TimestampField = t.TypeOf; -export const TimestampField = t.string; // should be non-empty string? - -export type TiebreakerField = t.TypeOf; -export const TiebreakerField = t.string; // should be non-empty string? diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/index.ts index 6fbe808a0eb48..a112f6ca1b29f 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/index.ts @@ -6,10 +6,3 @@ */ export * from './common_attributes'; - -export * from './eql_attributes'; -export * from './new_terms_attributes'; -export * from './query_attributes'; -export * from './threshold_attributes'; - -export * from './rule_schemas'; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/new_terms_attributes.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/new_terms_attributes.ts deleted file mode 100644 index 6d9f39011b675..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/new_terms_attributes.ts +++ /dev/null @@ -1,25 +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 * as t from 'io-ts'; -import { LimitedSizeArray, NonEmptyString } from '@kbn/securitysolution-io-ts-types'; -import { MAX_NUMBER_OF_NEW_TERMS_FIELDS } from '../../../../constants'; - -// Attributes specific to New Terms rules - -/** - * New terms rule type supports a limited number of fields. Max number of fields is 3 and defined in common constants as MAX_NUMBER_OF_NEW_TERMS_FIELDS - */ -export type NewTermsFields = t.TypeOf; -export const NewTermsFields = LimitedSizeArray({ - codec: t.string, - minSize: 1, - maxSize: MAX_NUMBER_OF_NEW_TERMS_FIELDS, -}); - -export type HistoryWindowStart = t.TypeOf; -export const HistoryWindowStart = NonEmptyString; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/query_attributes.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/query_attributes.ts deleted file mode 100644 index 8ee40e7a507d4..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/query_attributes.ts +++ /dev/null @@ -1,70 +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 * as t from 'io-ts'; -import { - LimitedSizeArray, - PositiveIntegerGreaterThanZero, - enumeration, -} from '@kbn/securitysolution-io-ts-types'; -import { AlertSuppressionMissingFieldsStrategyEnum } from '../rule_schema/common_attributes.gen'; - -export type AlertSuppressionMissingFields = t.TypeOf; -export const AlertSuppressionMissingFields = enumeration( - 'AlertSuppressionMissingFields', - AlertSuppressionMissingFieldsStrategyEnum -); - -export const AlertSuppressionGroupBy = LimitedSizeArray({ - codec: t.string, - minSize: 1, - maxSize: 3, -}); - -export const AlertSuppressionDuration = t.type({ - value: PositiveIntegerGreaterThanZero, - unit: t.keyof({ - s: null, - m: null, - h: null, - }), -}); - -/** - * Schema for fields relating to alert suppression, which enables limiting the number of alerts per entity. - * e.g. group_by: ['host.name'] would create only one alert per value of host.name. The created alert - * contains metadata about how many other candidate alerts with the same host.name value were suppressed. - */ -export type AlertSuppression = t.TypeOf; -export const AlertSuppression = t.intersection([ - t.exact( - t.type({ - group_by: AlertSuppressionGroupBy, - }) - ), - t.exact( - t.partial({ - duration: AlertSuppressionDuration, - missing_fields_strategy: AlertSuppressionMissingFields, - }) - ), -]); - -export type AlertSuppressionCamel = t.TypeOf; -export const AlertSuppressionCamel = t.intersection([ - t.exact( - t.type({ - groupBy: AlertSuppressionGroupBy, - }) - ), - t.exact( - t.partial({ - duration: AlertSuppressionDuration, - missingFieldsStrategy: AlertSuppressionMissingFields, - }) - ), -]); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/response_actions.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/response_actions.ts deleted file mode 100644 index e4164c6d2bb04..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/response_actions.ts +++ /dev/null @@ -1,74 +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 { arrayQueries, ecsMapping } from '@kbn/osquery-io-ts-types'; -import * as t from 'io-ts'; - -// to enable using RESPONSE_ACTION_API_COMMANDS_NAMES as a type -function keyObject(arr: T): { [K in T[number]]: null } { - return Object.fromEntries(arr.map((v) => [v, null])) as never; -} - -export type EndpointParams = t.TypeOf; -export const EndpointParams = t.type({ - // TODO: TC- change these when we go GA with automated process actions - command: t.keyof(keyObject(['isolate', 'kill-process', 'suspend-process'])), - // command: t.keyof(keyObject(ENABLED_AUTOMATED_RESPONSE_ACTION_COMMANDS)), - comment: t.union([t.string, t.undefined]), -}); - -export const OsqueryParams = t.type({ - query: t.union([t.string, t.undefined]), - ecs_mapping: t.union([ecsMapping, t.undefined]), - queries: t.union([arrayQueries, t.undefined]), - pack_id: t.union([t.string, t.undefined]), - saved_query_id: t.union([t.string, t.undefined]), -}); - -export const OsqueryParamsCamelCase = t.type({ - query: t.union([t.string, t.undefined]), - ecsMapping: t.union([ecsMapping, t.undefined]), - queries: t.union([arrayQueries, t.undefined]), - packId: t.union([t.string, t.undefined]), - savedQueryId: t.union([t.string, t.undefined]), -}); - -// When we create new response action types, create a union of types -export type RuleResponseOsqueryAction = t.TypeOf; -export const RuleResponseOsqueryAction = t.strict({ - actionTypeId: t.literal('.osquery'), - params: OsqueryParamsCamelCase, -}); - -export type RuleResponseEndpointAction = t.TypeOf; -export const RuleResponseEndpointAction = t.strict({ - actionTypeId: t.literal('.endpoint'), - params: EndpointParams, -}); - -export type RuleResponseAction = t.TypeOf; -const ResponseActionRuleParam = t.union([RuleResponseOsqueryAction, RuleResponseEndpointAction]); - -export const ResponseActionRuleParamsOrUndefined = t.union([ - t.array(ResponseActionRuleParam), - t.undefined, -]); - -// When we create new response action types, create a union of types -const OsqueryResponseAction = t.strict({ - action_type_id: t.literal('.osquery'), - params: OsqueryParams, -}); - -const EndpointResponseAction = t.strict({ - action_type_id: t.literal('.endpoint'), - params: EndpointParams, -}); - -export type ResponseAction = t.TypeOf; -export const ResponseAction = t.union([OsqueryResponseAction, EndpointResponseAction]); - -export const ResponseActionArray = t.array(ResponseAction); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/rule_schemas.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/rule_schemas.ts deleted file mode 100644 index 4ec9ca19ee399..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/rule_schemas.ts +++ /dev/null @@ -1,388 +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 * as t from 'io-ts'; - -import { - concurrent_searches, - items_per_search, - machine_learning_job_id, - RiskScore, - RiskScoreMapping, - RuleActionArray, - RuleActionThrottle, - RuleInterval, - RuleIntervalFrom, - RuleIntervalTo, - Severity, - SeverityMapping, - threat_filters, - threat_index, - threat_indicator_path, - threat_mapping, - threat_query, -} from '@kbn/securitysolution-io-ts-alerting-types'; -import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; -import { ResponseActionArray } from './response_actions'; - -import { anomaly_threshold, saved_id } from '../schemas'; - -import { - AlertsIndex, - AlertsIndexNamespace, - BuildingBlockType, - DataViewId, - ExceptionListArray, - IndexPatternArray, - InvestigationFields, - InvestigationGuide, - IsRuleEnabled, - MaxSignals, - RuleAuthorArray, - RuleDescription, - RuleFalsePositiveArray, - RuleFilterArray, - RuleLicense, - RuleMetadata, - RuleName, - RuleNameOverride, - RuleQuery, - RuleReferenceArray, - RuleSignatureId, - RuleTagArray, - RuleVersion, - SavedObjectResolveAliasPurpose, - SavedObjectResolveAliasTargetId, - SavedObjectResolveOutcome, - ThreatArray, - TimelineTemplateId, - TimelineTemplateTitle, - TimestampOverride, - TimestampOverrideFallbackDisabled, -} from './common_attributes'; -import { EventCategoryOverride, TiebreakerField, TimestampField } from './eql_attributes'; -import { HistoryWindowStart, NewTermsFields } from './new_terms_attributes'; -import { AlertSuppression } from './query_attributes'; -import { Threshold } from './threshold_attributes'; - -export const buildRuleSchemas = < - Required extends t.Props, - Optional extends t.Props, - Defaultable extends t.Props ->({ - required, - optional, - defaultable, -}: { - required: Required; - optional: Optional; - defaultable: Defaultable; -}) => ({ - create: t.intersection([ - t.exact(t.type(required)), - t.exact(t.partial(optional)), - t.exact(t.partial(defaultable)), - ]), - patch: t.intersection([t.partial(required), t.partial(optional), t.partial(defaultable)]), - response: t.intersection([ - t.exact(t.type(required)), - // This bit of logic is to force all fields to be accounted for in conversions from the internal - // rule schema to the response schema. Rather than use `t.partial`, which makes each field optional, - // we make each field required but possibly undefined. The result is that if a field is forgotten in - // the conversion from internal schema to response schema TS will report an error. If we just used t.partial - // instead, then optional fields can be accidentally omitted from the conversion - and any actual values - // in those fields internally will be stripped in the response. - t.exact(t.type(orUndefined(optional))), - t.exact(t.type(defaultable)), - ]), -}); - -export type OrUndefined

    = { - [K in keyof P]: P[K] | t.UndefinedC; -}; - -export const orUndefined =

    (props: P): OrUndefined

    => { - return Object.keys(props).reduce((acc, key) => { - acc[key] = t.union([props[key], t.undefined]); - return acc; - }, {}) as OrUndefined

    ; -}; - -// ------------------------------------------------------------------------------------------------- -// Base schema - -export const baseSchema = buildRuleSchemas({ - required: { - name: RuleName, - description: RuleDescription, - risk_score: RiskScore, - severity: Severity, - }, - optional: { - // Field overrides - rule_name_override: RuleNameOverride, - timestamp_override: TimestampOverride, - timestamp_override_fallback_disabled: TimestampOverrideFallbackDisabled, - // Timeline template - timeline_id: TimelineTemplateId, - timeline_title: TimelineTemplateTitle, - // Attributes related to SavedObjectsClient.resolve API - outcome: SavedObjectResolveOutcome, - alias_target_id: SavedObjectResolveAliasTargetId, - alias_purpose: SavedObjectResolveAliasPurpose, - // Misc attributes - license: RuleLicense, - note: InvestigationGuide, - building_block_type: BuildingBlockType, - output_index: AlertsIndex, - namespace: AlertsIndexNamespace, - meta: RuleMetadata, - investigation_fields: InvestigationFields, - // Throttle - throttle: RuleActionThrottle, - }, - defaultable: { - // Main attributes - version: RuleVersion, - tags: RuleTagArray, - enabled: IsRuleEnabled, - // Field overrides - risk_score_mapping: RiskScoreMapping, - severity_mapping: SeverityMapping, - // Rule schedule - interval: RuleInterval, - from: RuleIntervalFrom, - to: RuleIntervalTo, - // Rule actions - actions: RuleActionArray, - // Rule exceptions - exceptions_list: ExceptionListArray, - // Misc attributes - author: RuleAuthorArray, - false_positives: RuleFalsePositiveArray, - references: RuleReferenceArray, - // maxSignals not used in ML rules but probably should be used - max_signals: MaxSignals, - threat: ThreatArray, - }, -}); - -export type DurationMetric = t.TypeOf; -export const DurationMetric = PositiveInteger; - -export type RuleExecutionMetrics = t.TypeOf; - -/** - @property total_search_duration_ms - "total time spent performing ES searches as measured by Kibana; - includes network latency and time spent serializing/deserializing request/response", - @property total_indexing_duration_ms - "total time spent indexing documents during current rule execution cycle", - @property total_enrichment_duration_ms - total time spent enriching documents during current rule execution cycle - @property execution_gap_duration_s - "duration in seconds of execution gap" -*/ -export const RuleExecutionMetrics = t.partial({ - total_search_duration_ms: DurationMetric, - total_indexing_duration_ms: DurationMetric, - total_enrichment_duration_ms: DurationMetric, - execution_gap_duration_s: DurationMetric, -}); - -export type BaseCreateProps = t.TypeOf; -export const BaseCreateProps = baseSchema.create; - -// ------------------------------------------------------------------------------------------------- -// Shared schemas - -// "Shared" types are the same across all rule types, and built from "baseSchema" above -// with some variations for each route. These intersect with type specific schemas below -// to create the full schema for each route. - -export type SharedCreateProps = t.TypeOf; -export const SharedCreateProps = t.intersection([ - baseSchema.create, - t.exact(t.partial({ rule_id: RuleSignatureId })), -]); - -// ------------------------------------------------------------------------------------------------- -// EQL rule schema - -export type KqlQueryLanguage = t.TypeOf; -export const KqlQueryLanguage = t.keyof({ kuery: null, lucene: null }); - -export type EqlQueryLanguage = t.TypeOf; -export const EqlQueryLanguage = t.literal('eql'); - -const eqlSchema = buildRuleSchemas({ - required: { - type: t.literal('eql'), - language: EqlQueryLanguage, - query: RuleQuery, - }, - optional: { - index: IndexPatternArray, - data_view_id: DataViewId, - filters: RuleFilterArray, - timestamp_field: TimestampField, - event_category_override: EventCategoryOverride, - tiebreaker_field: TiebreakerField, - }, - defaultable: {}, -}); - -// ------------------------------------------------------------------------------------------------- -// ES|QL rule schema - -export type EsqlQueryLanguage = t.TypeOf; -export const EsqlQueryLanguage = t.literal('esql'); - -const esqlSchema = buildRuleSchemas({ - required: { - type: t.literal('esql'), - language: EsqlQueryLanguage, - query: RuleQuery, - }, - optional: {}, - defaultable: {}, -}); - -// ------------------------------------------------------------------------------------------------- -// Indicator Match rule schema - -const threatMatchSchema = buildRuleSchemas({ - required: { - type: t.literal('threat_match'), - query: RuleQuery, - threat_query, - threat_mapping, - threat_index, - }, - optional: { - index: IndexPatternArray, - data_view_id: DataViewId, - filters: RuleFilterArray, - saved_id, - threat_filters, - threat_indicator_path, - threat_language: KqlQueryLanguage, - concurrent_searches, - items_per_search, - }, - defaultable: { - language: KqlQueryLanguage, - }, -}); - -// ------------------------------------------------------------------------------------------------- -// Custom Query rule schema - -const querySchema = buildRuleSchemas({ - required: { - type: t.literal('query'), - }, - optional: { - index: IndexPatternArray, - data_view_id: DataViewId, - filters: RuleFilterArray, - saved_id, - response_actions: ResponseActionArray, - alert_suppression: AlertSuppression, - }, - defaultable: { - query: RuleQuery, - language: KqlQueryLanguage, - }, -}); - -// ------------------------------------------------------------------------------------------------- -// Saved Query rule schema - -const savedQuerySchema = buildRuleSchemas({ - required: { - type: t.literal('saved_query'), - saved_id, - }, - optional: { - // Having language, query, and filters possibly defined adds more code confusion and probably user confusion - // if the saved object gets deleted for some reason - index: IndexPatternArray, - data_view_id: DataViewId, - query: RuleQuery, - filters: RuleFilterArray, - response_actions: ResponseActionArray, - alert_suppression: AlertSuppression, - }, - defaultable: { - language: KqlQueryLanguage, - }, -}); - -// ------------------------------------------------------------------------------------------------- -// Threshold rule schema - -const thresholdSchema = buildRuleSchemas({ - required: { - type: t.literal('threshold'), - query: RuleQuery, - threshold: Threshold, - }, - optional: { - index: IndexPatternArray, - data_view_id: DataViewId, - filters: RuleFilterArray, - saved_id, - }, - defaultable: { - language: KqlQueryLanguage, - }, -}); - -// ------------------------------------------------------------------------------------------------- -// Machine Learning rule schema - -const machineLearningSchema = buildRuleSchemas({ - required: { - type: t.literal('machine_learning'), - anomaly_threshold, - machine_learning_job_id, - }, - optional: {}, - defaultable: {}, -}); - -// ------------------------------------------------------------------------------------------------- -// New Terms rule schema - -const newTermsSchema = buildRuleSchemas({ - required: { - type: t.literal('new_terms'), - query: RuleQuery, - new_terms_fields: NewTermsFields, - history_window_start: HistoryWindowStart, - }, - optional: { - index: IndexPatternArray, - data_view_id: DataViewId, - filters: RuleFilterArray, - }, - defaultable: { - language: KqlQueryLanguage, - }, -}); - -// ------------------------------------------------------------------------------------------------- -// Combined type specific schemas - -export type TypeSpecificCreateProps = t.TypeOf; -export const TypeSpecificCreateProps = t.union([ - eqlSchema.create, - esqlSchema.create, - threatMatchSchema.create, - querySchema.create, - savedQuerySchema.create, - thresholdSchema.create, - machineLearningSchema.create, - newTermsSchema.create, -]); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/threshold_attributes.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/threshold_attributes.ts deleted file mode 100644 index eb5639c97ab3e..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/threshold_attributes.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; -import { PositiveInteger, PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; - -// Attributes specific to Threshold rules - -const thresholdField = t.exact( - t.type({ - field: t.union([t.string, t.array(t.string)]), // Covers pre- and post-7.12 - value: PositiveIntegerGreaterThanZero, - }) -); - -const thresholdFieldNormalized = t.exact( - t.type({ - field: t.array(t.string), - value: PositiveIntegerGreaterThanZero, - }) -); - -const thresholdCardinalityField = t.exact( - t.type({ - field: t.string, - value: PositiveInteger, - }) -); - -export type Threshold = t.TypeOf; -export const Threshold = t.intersection([ - thresholdField, - t.exact( - t.partial({ - cardinality: t.array(thresholdCardinalityField), - }) - ), -]); - -export type ThresholdNormalized = t.TypeOf; -export const ThresholdNormalized = t.intersection([ - thresholdFieldNormalized, - t.exact( - t.partial({ - cardinality: t.array(thresholdCardinalityField), - }) - ), -]); - -export type ThresholdWithCardinality = t.TypeOf; -export const ThresholdWithCardinality = t.intersection([ - thresholdFieldNormalized, - t.exact( - t.type({ - cardinality: t.array(thresholdCardinalityField), - }) - ), -]); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/schemas.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/schemas.ts index 68a6dbb461673..12973846c1f23 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/schemas.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/schemas.ts @@ -7,89 +7,76 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import * as t from 'io-ts'; -import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; - -export const file_name = t.string; -export type FileName = t.TypeOf; - -export const exclude_export_details = t.boolean; -export type ExcludeExportDetails = t.TypeOf; - -export const saved_id = t.string; - -export const savedIdOrUndefined = t.union([saved_id, t.undefined]); -export type SavedIdOrUndefined = t.TypeOf; - -export const anomaly_threshold = PositiveInteger; - -export const status = t.keyof({ - open: null, - closed: null, - acknowledged: null, - 'in-progress': null, -}); -export type Status = t.TypeOf; - -export const conflicts = t.keyof({ abort: null, proceed: null }); - -export const signal_ids = t.array(t.string); -export type SignalIds = t.TypeOf; - -// TODO: Can this be more strict or is this is the set of all Elastic Queries? -export const signal_status_query = t.object; - -export const alert_tag_ids = t.array(t.string); -export type AlertTagIds = t.TypeOf; - -export const indexRecord = t.record( - t.string, - t.type({ - all: t.boolean, - maintenance: t.boolean, - read: t.boolean, - create_index: t.boolean, - index: t.boolean, - monitor: t.boolean, - delete: t.boolean, - manage: t.boolean, - delete_index: t.boolean, - create_doc: t.boolean, - view_index_metadata: t.boolean, - create: t.boolean, - write: t.boolean, +import { z } from 'zod'; + +export type FileName = z.infer; +export const file_name = z.string(); + +export type ExcludeExportDetails = z.infer; +export const exclude_export_details = z.boolean(); + +export const saved_id = z.string(); + +export type SavedIdOrUndefined = z.infer; +export const savedIdOrUndefined = saved_id.optional(); + +export const status = z.enum(['open', 'closed', 'acknowledged', 'in-progress']); +export type Status = z.infer; + +export const signal_ids = z.array(z.string()); +export type SignalIds = z.infer; + +export const alert_tag_ids = z.array(z.string()); +export type AlertTagIds = z.infer; + +export const indexRecord = z.record( + z.string(), + z.object({ + all: z.boolean(), + maintenance: z.boolean(), + read: z.boolean(), + create_index: z.boolean(), + index: z.boolean(), + monitor: z.boolean(), + delete: z.boolean(), + manage: z.boolean(), + delete_index: z.boolean(), + create_doc: z.boolean(), + view_index_metadata: z.boolean(), + create: z.boolean(), + write: z.boolean(), }) ); -export const privilege = t.type({ - username: t.string, - has_all_requested: t.boolean, - cluster: t.type({ - monitor_ml: t.boolean, - manage_index_templates: t.boolean, - monitor_transform: t.boolean, - manage_security: t.boolean, - manage_own_api_key: t.boolean, - all: t.boolean, - monitor: t.boolean, - manage: t.boolean, - manage_transform: t.boolean, - manage_ml: t.boolean, - manage_pipeline: t.boolean, +export const privilege = z.object({ + username: z.string(), + has_all_requested: z.boolean(), + cluster: z.object({ + monitor_ml: z.boolean(), + manage_index_templates: z.boolean(), + monitor_transform: z.boolean(), + manage_security: z.boolean(), + manage_own_api_key: z.boolean(), + all: z.boolean(), + monitor: z.boolean(), + manage: z.boolean(), + manage_transform: z.boolean(), + manage_ml: z.boolean(), + manage_pipeline: z.boolean(), }), index: indexRecord, - is_authenticated: t.boolean, - has_encryption_key: t.boolean, + is_authenticated: z.boolean(), + has_encryption_key: z.boolean(), }); -export type Privilege = t.TypeOf; +export type Privilege = z.infer; -export const alert_tags = t.type({ - tags_to_add: t.array(t.string), - tags_to_remove: t.array(t.string), +export const alert_tags = z.object({ + tags_to_add: z.array(z.string()), + tags_to_remove: z.array(z.string()), }); -export type AlertTags = t.TypeOf; +export type AlertTags = z.infer; -export const user_search_term = t.string; -export type UserSearchTerm = t.TypeOf; +export const user_search_term = z.string(); +export type UserSearchTerm = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts index a2b514676767b..635dfdf45c1c4 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts @@ -14,7 +14,6 @@ export * from './review_rule_installation/review_rule_installation_route'; export * from './review_rule_upgrade/review_rule_upgrade_route'; export * from './urls'; export * from './model/aggregated_prebuilt_rules_error'; -export * from './model/diff/diffable_rule/build_schema'; export * from './model/diff/diffable_rule/diffable_field_types'; export * from './model/diff/diffable_rule/diffable_rule'; export * from './model/diff/rule_diff/fields_diff'; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/build_schema.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/build_schema.ts deleted file mode 100644 index b1d7752fb9f89..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/build_schema.ts +++ /dev/null @@ -1,25 +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 * as t from 'io-ts'; -// TODO https://github.com/elastic/security-team/issues/7491 -// eslint-disable-next-line no-restricted-imports -import { orUndefined } from '../../../../model/rule_schema_legacy'; - -interface RuleFields { - required: TRequired; - optional: TOptional; -} - -export const buildSchema = ( - fields: RuleFields -) => { - return t.intersection([ - t.exact(t.type(fields.required)), - t.exact(t.type(orUndefined(fields.optional))), - ]); -}; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types.ts index 299b4a7d7b394..aa64c1d12185a 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_field_types.ts @@ -4,25 +4,23 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { z } from 'zod'; -import * as t from 'io-ts'; -import { TimeDuration } from '@kbn/securitysolution-io-ts-types'; -// TODO https://github.com/elastic/security-team/issues/7491 -// eslint-disable-next-line no-restricted-imports import { BuildingBlockType, DataViewId, IndexPatternArray, KqlQueryLanguage, RuleFilterArray, - RuleNameOverride as RuleNameOverrideFieldName, + RuleNameOverride, RuleQuery, + SavedQueryId, TimelineTemplateId, TimelineTemplateTitle, - TimestampOverride as TimestampOverrideFieldName, + TimestampOverride, TimestampOverrideFallbackDisabled, -} from '../../../../model/rule_schema_legacy'; -import { saved_id } from '../../../../model/schemas'; +} from '../../../../model/rule_schema'; +import { TimeDuration } from '../../../../model/rule_schema/time_duration'; // ------------------------------------------------------------------------------------------------- // Rule data source @@ -32,24 +30,23 @@ export enum DataSourceType { 'data_view' = 'data_view', } -export type DataSourceIndexPatterns = t.TypeOf; -export const DataSourceIndexPatterns = t.exact( - t.type({ - type: t.literal(DataSourceType.index_patterns), - index_patterns: IndexPatternArray, - }) -); - -export type DataSourceDataView = t.TypeOf; -export const DataSourceDataView = t.exact( - t.type({ - type: t.literal(DataSourceType.data_view), - data_view_id: DataViewId, - }) -); - -export type RuleDataSource = t.TypeOf; -export const RuleDataSource = t.union([DataSourceIndexPatterns, DataSourceDataView]); +export type DataSourceIndexPatterns = z.infer; +export const DataSourceIndexPatterns = z.object({ + type: z.literal(DataSourceType.index_patterns), + index_patterns: IndexPatternArray, +}); + +export type DataSourceDataView = z.infer; +export const DataSourceDataView = z.object({ + type: z.literal(DataSourceType.data_view), + data_view_id: DataViewId, +}); + +export type RuleDataSource = z.infer; +export const RuleDataSource = z.discriminatedUnion('type', [ + DataSourceIndexPatterns, + DataSourceDataView, +]); // ------------------------------------------------------------------------------------------------- // Rule data query @@ -59,93 +56,75 @@ export enum KqlQueryType { 'saved_query' = 'saved_query', } -export type InlineKqlQuery = t.TypeOf; -export const InlineKqlQuery = t.exact( - t.type({ - type: t.literal(KqlQueryType.inline_query), - query: RuleQuery, - language: KqlQueryLanguage, - filters: RuleFilterArray, - }) -); - -export type SavedKqlQuery = t.TypeOf; -export const SavedKqlQuery = t.exact( - t.type({ - type: t.literal(KqlQueryType.saved_query), - saved_query_id: saved_id, - }) -); - -export type RuleKqlQuery = t.TypeOf; -export const RuleKqlQuery = t.union([InlineKqlQuery, SavedKqlQuery]); - -export type RuleEqlQuery = t.TypeOf; -export const RuleEqlQuery = t.exact( - t.type({ - query: RuleQuery, - language: t.literal('eql'), - filters: RuleFilterArray, - }) -); - -export type RuleEsqlQuery = t.TypeOf; -export const RuleEsqlQuery = t.exact( - t.type({ - query: RuleQuery, - language: t.literal('esql'), - }) -); +export type InlineKqlQuery = z.infer; +export const InlineKqlQuery = z.object({ + type: z.literal(KqlQueryType.inline_query), + query: RuleQuery, + language: KqlQueryLanguage, + filters: RuleFilterArray, +}); + +export type SavedKqlQuery = z.infer; +export const SavedKqlQuery = z.object({ + type: z.literal(KqlQueryType.saved_query), + saved_query_id: SavedQueryId, +}); + +export type RuleKqlQuery = z.infer; +export const RuleKqlQuery = z.discriminatedUnion('type', [InlineKqlQuery, SavedKqlQuery]); + +export type RuleEqlQuery = z.infer; +export const RuleEqlQuery = z.object({ + query: RuleQuery, + language: z.literal('eql'), + filters: RuleFilterArray, +}); + +export type RuleEsqlQuery = z.infer; +export const RuleEsqlQuery = z.object({ + query: RuleQuery, + language: z.literal('esql'), +}); // ------------------------------------------------------------------------------------------------- // Rule schedule -export type RuleSchedule = t.TypeOf; -export const RuleSchedule = t.exact( - t.type({ - interval: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }), - lookback: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }), - }) -); +export type RuleSchedule = z.infer; +export const RuleSchedule = z.object({ + interval: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }), + lookback: TimeDuration({ allowedUnits: ['s', 'm', 'h'] }), +}); // ------------------------------------------------------------------------------------------------- // Rule name override -export type RuleNameOverrideObject = t.TypeOf; -export const RuleNameOverrideObject = t.exact( - t.type({ - field_name: RuleNameOverrideFieldName, - }) -); +export type RuleNameOverrideObject = z.infer; +export const RuleNameOverrideObject = z.object({ + field_name: RuleNameOverride, +}); // ------------------------------------------------------------------------------------------------- // Timestamp override -export type TimestampOverrideObject = t.TypeOf; -export const TimestampOverrideObject = t.exact( - t.type({ - field_name: TimestampOverrideFieldName, - fallback_disabled: TimestampOverrideFallbackDisabled, - }) -); +export type TimestampOverrideObject = z.infer; +export const TimestampOverrideObject = z.object({ + field_name: TimestampOverride, + fallback_disabled: TimestampOverrideFallbackDisabled, +}); // ------------------------------------------------------------------------------------------------- // Reference to a timeline template -export type TimelineTemplateReference = t.TypeOf; -export const TimelineTemplateReference = t.exact( - t.type({ - timeline_id: TimelineTemplateId, - timeline_title: TimelineTemplateTitle, - }) -); +export type TimelineTemplateReference = z.infer; +export const TimelineTemplateReference = z.object({ + timeline_id: TimelineTemplateId, + timeline_title: TimelineTemplateTitle, +}); // ------------------------------------------------------------------------------------------------- // Building block -export type BuildingBlockObject = t.TypeOf; -export const BuildingBlockObject = t.exact( - t.type({ - type: BuildingBlockType, - }) -); +export type BuildingBlockObject = z.infer; +export const BuildingBlockObject = z.object({ + type: BuildingBlockType, +}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule.ts index 9bb6fc10031d2..3d58f1e0b7da5 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/diffable_rule/diffable_rule.ts @@ -5,212 +5,164 @@ * 2.0. */ -import * as t from 'io-ts'; +import { z } from 'zod'; import { - concurrent_searches, - items_per_search, - machine_learning_job_id, - RiskScore, - RiskScoreMapping, - RuleActionArray, - RuleActionThrottle, - Severity, - SeverityMapping, - threat_index, - threat_indicator_path, - threat_mapping, -} from '@kbn/securitysolution-io-ts-alerting-types'; - -// TODO https://github.com/elastic/security-team/issues/7491 -// eslint-disable-next-line no-restricted-imports -import { - AlertSuppression, + AnomalyThreshold, + ConcurrentSearches, EventCategoryOverride, - ExceptionListArray, HistoryWindowStart, InvestigationGuide, + ItemsPerSearch, + MachineLearningJobId, MaxSignals, NewTermsFields, RelatedIntegrationArray, RequiredFieldArray, + RiskScore, + RiskScoreMapping, RuleAuthorArray, RuleDescription, + RuleExceptionList, RuleFalsePositiveArray, RuleLicense, - RuleMetadata, RuleName, RuleReferenceArray, RuleSignatureId, RuleTagArray, RuleVersion, SetupGuide, + Severity, + SeverityMapping, ThreatArray, + ThreatIndex, + ThreatIndicatorPath, + ThreatMapping, Threshold, TiebreakerField, TimestampField, -} from '../../../../model/rule_schema_legacy'; +} from '../../../../model/rule_schema'; import { BuildingBlockObject, + InlineKqlQuery, + RuleDataSource, RuleEqlQuery, RuleEsqlQuery, - InlineKqlQuery, RuleKqlQuery, - RuleDataSource, RuleNameOverrideObject, RuleSchedule, TimelineTemplateReference, TimestampOverrideObject, } from './diffable_field_types'; -import { buildSchema } from './build_schema'; -import { anomaly_threshold } from '../../../../model/schemas'; - -export type DiffableCommonFields = t.TypeOf; -export const DiffableCommonFields = buildSchema({ - required: { - // Technical fields - // NOTE: We might consider removing them from the schema and returning from the API - // not via the fields diff, but via dedicated properties in the response body. - rule_id: RuleSignatureId, - version: RuleVersion, - meta: RuleMetadata, - - // Main domain fields - name: RuleName, - tags: RuleTagArray, - description: RuleDescription, - severity: Severity, - severity_mapping: SeverityMapping, - risk_score: RiskScore, - risk_score_mapping: RiskScoreMapping, - - // About -> Advanced settings - references: RuleReferenceArray, - false_positives: RuleFalsePositiveArray, - threat: ThreatArray, - note: InvestigationGuide, - setup: SetupGuide, - related_integrations: RelatedIntegrationArray, - required_fields: RequiredFieldArray, - author: RuleAuthorArray, - license: RuleLicense, - - // Other domain fields - rule_schedule: RuleSchedule, // NOTE: new field - actions: RuleActionArray, - throttle: RuleActionThrottle, - exceptions_list: ExceptionListArray, - max_signals: MaxSignals, - }, - optional: { - rule_name_override: RuleNameOverrideObject, // NOTE: new field - timestamp_override: TimestampOverrideObject, // NOTE: new field - timeline_template: TimelineTemplateReference, // NOTE: new field - building_block: BuildingBlockObject, // NOTE: new field - }, +export type DiffableCommonFields = z.infer; +export const DiffableCommonFields = z.object({ + // Technical fields + // NOTE: We might consider removing them from the schema and returning from the API + // not via the fields diff, but via dedicated properties in the response body. + rule_id: RuleSignatureId, + version: RuleVersion, + + // Main domain fields + name: RuleName, + tags: RuleTagArray, + description: RuleDescription, + severity: Severity, + severity_mapping: SeverityMapping, + risk_score: RiskScore, + risk_score_mapping: RiskScoreMapping, + + // About -> Advanced settings + references: RuleReferenceArray, + false_positives: RuleFalsePositiveArray, + threat: ThreatArray, + note: InvestigationGuide, + setup: SetupGuide, + related_integrations: RelatedIntegrationArray, + required_fields: RequiredFieldArray, + author: RuleAuthorArray, + license: RuleLicense, + + // Other domain fields + rule_schedule: RuleSchedule, // NOTE: new field + exceptions_list: z.array(RuleExceptionList), + max_signals: MaxSignals, + + // Optional fields + rule_name_override: RuleNameOverrideObject.optional(), // NOTE: new field + timestamp_override: TimestampOverrideObject.optional(), // NOTE: new field + timeline_template: TimelineTemplateReference.optional(), // NOTE: new field + building_block: BuildingBlockObject.optional(), // NOTE: new field }); -export type DiffableCustomQueryFields = t.TypeOf; -export const DiffableCustomQueryFields = buildSchema({ - required: { - type: t.literal('query'), - kql_query: RuleKqlQuery, // NOTE: new field - }, - optional: { - data_source: RuleDataSource, // NOTE: new field - alert_suppression: AlertSuppression, - }, +export type DiffableCustomQueryFields = z.infer; +export const DiffableCustomQueryFields = z.object({ + type: z.literal('query'), + kql_query: RuleKqlQuery, // NOTE: new field + data_source: RuleDataSource.optional(), // NOTE: new field }); -export type DiffableSavedQueryFields = t.TypeOf; -export const DiffableSavedQueryFields = buildSchema({ - required: { - type: t.literal('saved_query'), - kql_query: RuleKqlQuery, // NOTE: new field - }, - optional: { - data_source: RuleDataSource, // NOTE: new field - alert_suppression: AlertSuppression, - }, +export type DiffableSavedQueryFields = z.infer; +export const DiffableSavedQueryFields = z.object({ + type: z.literal('saved_query'), + kql_query: RuleKqlQuery, // NOTE: new field + data_source: RuleDataSource.optional(), // NOTE: new field }); -export type DiffableEqlFields = t.TypeOf; -export const DiffableEqlFields = buildSchema({ - required: { - type: t.literal('eql'), - eql_query: RuleEqlQuery, // NOTE: new field - }, - optional: { - data_source: RuleDataSource, // NOTE: new field - event_category_override: EventCategoryOverride, - timestamp_field: TimestampField, - tiebreaker_field: TiebreakerField, - }, +export type DiffableEqlFields = z.infer; +export const DiffableEqlFields = z.object({ + type: z.literal('eql'), + eql_query: RuleEqlQuery, // NOTE: new field + data_source: RuleDataSource.optional(), // NOTE: new field + event_category_override: EventCategoryOverride.optional(), + timestamp_field: TimestampField.optional(), + tiebreaker_field: TiebreakerField.optional(), }); -export type DiffableEsqlFields = t.TypeOf; -export const DiffableEsqlFields = buildSchema({ - required: { - type: t.literal('esql'), - esql_query: RuleEsqlQuery, // NOTE: new field - }, - // this is a new type of rule, no prebuilt rules created yet. - // new properties might be added here during further rule type development - optional: {}, +// this is a new type of rule, no prebuilt rules created yet. +// new properties might be added here during further rule type development +export type DiffableEsqlFields = z.infer; +export const DiffableEsqlFields = z.object({ + type: z.literal('esql'), + esql_query: RuleEsqlQuery, // NOTE: new field }); -export type DiffableThreatMatchFields = t.TypeOf; -export const DiffableThreatMatchFields = buildSchema({ - required: { - type: t.literal('threat_match'), - kql_query: RuleKqlQuery, // NOTE: new field - threat_query: InlineKqlQuery, // NOTE: new field - threat_index, - threat_mapping, - }, - optional: { - data_source: RuleDataSource, // NOTE: new field - threat_indicator_path, - concurrent_searches, // Should combine concurrent_searches and items_per_search? - items_per_search, - }, +export type DiffableThreatMatchFields = z.infer; +export const DiffableThreatMatchFields = z.object({ + type: z.literal('threat_match'), + kql_query: RuleKqlQuery, // NOTE: new field + threat_query: InlineKqlQuery, // NOTE: new field + threat_index: ThreatIndex, + threat_mapping: ThreatMapping, + data_source: RuleDataSource.optional(), // NOTE: new field + threat_indicator_path: ThreatIndicatorPath.optional(), + concurrent_searches: ConcurrentSearches.optional(), + items_per_search: ItemsPerSearch.optional(), }); -export type DiffableThresholdFields = t.TypeOf; -export const DiffableThresholdFields = buildSchema({ - required: { - type: t.literal('threshold'), - kql_query: RuleKqlQuery, // NOTE: new field - threshold: Threshold, - }, - optional: { - data_source: RuleDataSource, // NOTE: new field - }, +export type DiffableThresholdFields = z.infer; +export const DiffableThresholdFields = z.object({ + type: z.literal('threshold'), + kql_query: RuleKqlQuery, // NOTE: new field + threshold: Threshold, + data_source: RuleDataSource.optional(), // NOTE: new field }); -export type DiffableMachineLearningFields = t.TypeOf; -export const DiffableMachineLearningFields = buildSchema({ - required: { - type: t.literal('machine_learning'), - machine_learning_job_id, - anomaly_threshold, - }, - optional: {}, +export type DiffableMachineLearningFields = z.infer; +export const DiffableMachineLearningFields = z.object({ + type: z.literal('machine_learning'), + machine_learning_job_id: MachineLearningJobId, + anomaly_threshold: AnomalyThreshold, }); -export type DiffableNewTermsFields = t.TypeOf; -export const DiffableNewTermsFields = buildSchema({ - required: { - type: t.literal('new_terms'), - kql_query: InlineKqlQuery, // NOTE: new field - new_terms_fields: NewTermsFields, - history_window_start: HistoryWindowStart, - }, - optional: { - data_source: RuleDataSource, // NOTE: new field - }, +export type DiffableNewTermsFields = z.infer; +export const DiffableNewTermsFields = z.object({ + type: z.literal('new_terms'), + kql_query: InlineKqlQuery, // NOTE: new field + new_terms_fields: NewTermsFields, + history_window_start: HistoryWindowStart, + data_source: RuleDataSource.optional(), // NOTE: new field }); /** @@ -240,10 +192,10 @@ export const DiffableNewTermsFields = buildSchema({ * top-level fields. */ -export type DiffableRule = t.TypeOf; -export const DiffableRule = t.intersection([ +export type DiffableRule = z.infer; +const DiffableRule = z.intersection( DiffableCommonFields, - t.union([ + z.discriminatedUnion('type', [ DiffableCustomQueryFields, DiffableSavedQueryFields, DiffableEqlFields, @@ -252,8 +204,8 @@ export const DiffableRule = t.intersection([ DiffableThresholdFields, DiffableMachineLearningFields, DiffableNewTermsFields, - ]), -]); + ]) +); /** * This is a merge of all fields from all rule types into a single TS type. diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff.ts index a1b8a0ae8d104..9637faebe9c9c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/rule_diff/fields_diff.ts @@ -7,10 +7,10 @@ import type { ThreeWayDiff, ThreeWayDiffAlgorithm } from '../three_way_diff/three_way_diff'; -export type FieldsDiff = { +export type FieldsDiff = Required<{ [Field in keyof TObject]: ThreeWayDiff; -}; +}>; -export type FieldsDiffAlgorithmsFor = { +export type FieldsDiffAlgorithmsFor = Required<{ [Field in keyof TObject]: ThreeWayDiffAlgorithm; -}; +}>; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/index.ts index 7e550633d8efc..8abee3cbe83a6 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/index.ts @@ -6,7 +6,6 @@ */ export * from './aggregated_prebuilt_rules_error'; -export * from './diff/diffable_rule/build_schema'; export * from './diff/diffable_rule/diffable_field_types'; export * from './diff/diffable_rule/diffable_rule'; export * from './diff/rule_diff/fields_diff'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts index a5e5c060e7303..8bb76a19fa015 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts @@ -71,13 +71,3 @@ export const NoParametersRequestSchema = { body: schema.object({ ...BaseActionRequestSchema }), }; export type BaseActionRequestBody = TypeOf; - -export const KillOrSuspendProcessRequestSchema = { - body: schema.object({ - ...BaseActionRequestSchema, - parameters: schema.oneOf([ - schema.object({ pid: schema.number({ min: 1 }) }), - schema.object({ entity_id: schema.string({ minLength: 1 }) }), - ]), - }), -}; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts index 66dc4d5828ce0..ca6d9d5e91982 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts @@ -7,15 +7,20 @@ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { UploadActionRequestSchema } from '../..'; +import { + KillProcessRouteRequestSchema, + SuspendProcessRouteRequestSchema, + UploadActionRequestSchema, +} from '../..'; import { ExecuteActionRequestSchema } from '../execute_route'; import { EndpointActionGetFileSchema } from '../get_file_route'; import { ScanActionRequestSchema } from '../scan_route'; -import { KillOrSuspendProcessRequestSchema, NoParametersRequestSchema } from './base'; +import { NoParametersRequestSchema } from './base'; export const ResponseActionBodySchema = schema.oneOf([ NoParametersRequestSchema.body, - KillOrSuspendProcessRequestSchema.body, + KillProcessRouteRequestSchema.body, + SuspendProcessRouteRequestSchema.body, EndpointActionGetFileSchema.body, ExecuteActionRequestSchema.body, ScanActionRequestSchema.body, diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/kill_process_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/kill_process_route.ts index 8652623f93a57..f3c0d4e8f12be 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/kill_process_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/kill_process_route.ts @@ -5,6 +5,39 @@ * 2.0. */ -import { KillOrSuspendProcessRequestSchema } from './common/base'; +import { schema } from '@kbn/config-schema'; +import { BaseActionRequestSchema } from './common/base'; -export const KillProcessRouteRequestSchema = KillOrSuspendProcessRequestSchema; +// -------------------------------------------------- +// Tests for this module are at: +// x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts:604 +// -------------------------------------------------- + +export const KillProcessRouteRequestSchema = { + body: schema.object( + { + ...BaseActionRequestSchema, + parameters: schema.oneOf([ + schema.object({ pid: schema.number({ min: 1 }) }), + schema.object({ entity_id: schema.string({ minLength: 1 }) }), + + // Process Name currently applies only to SentinelOne (validated below) + schema.object({ process_name: schema.string({ minLength: 1 }) }), + ]), + }, + { + validate(bodyContent) { + if ('process_name' in bodyContent.parameters && bodyContent.agent_type !== 'sentinel_one') { + return `[parameters.process_name]: is not valid with agent type of ${bodyContent.agent_type}`; + } + + if ( + bodyContent.agent_type === 'sentinel_one' && + !('process_name' in bodyContent.parameters) + ) { + return `[parameters.process_name]: missing parameter for agent type of ${bodyContent.agent_type}`; + } + }, + } + ), +}; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/suspend_process_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/suspend_process_route.ts index 71801f4b979fc..81edb01197c69 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/suspend_process_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/suspend_process_route.ts @@ -5,6 +5,15 @@ * 2.0. */ -import { KillOrSuspendProcessRequestSchema } from './common/base'; +import { schema } from '@kbn/config-schema'; +import { BaseActionRequestSchema } from './common/base'; -export const SuspendProcessRouteRequestSchema = KillOrSuspendProcessRequestSchema; +export const SuspendProcessRouteRequestSchema = { + body: schema.object({ + ...BaseActionRequestSchema, + parameters: schema.oneOf([ + schema.object({ pid: schema.number({ min: 1 }) }), + schema.object({ entity_id: schema.string({ minLength: 1 }) }), + ]), + }), +}; diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts index 563633ed8413d..4ea9f59ea179d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts @@ -14,11 +14,13 @@ import { } from '../service/response_actions/constants'; import { createHapiReadableStreamMock } from '../../../server/endpoint/services/actions/mocks'; import type { HapiReadableStream } from '../../../server/types'; -import { EndpointActionListRequestSchema, UploadActionRequestSchema } from '../../api/endpoint'; import { - KillOrSuspendProcessRequestSchema, - NoParametersRequestSchema, -} from '../../api/endpoint/actions/common/base'; + EndpointActionListRequestSchema, + KillProcessRouteRequestSchema, + SuspendProcessRouteRequestSchema, + UploadActionRequestSchema, +} from '../../api/endpoint'; +import { NoParametersRequestSchema } from '../../api/endpoint/actions/common/base'; import { ExecuteActionRequestSchema } from '../../api/endpoint/actions/execute_route'; import { ScanActionRequestSchema } from '../../api/endpoint/actions/scan_route'; @@ -507,16 +509,20 @@ describe('actions schemas', () => { }); }); - describe('KillOrSuspendProcessRequestSchema', () => { + describe.each` + name | killOrSuspendSchema + ${'KillProcessRouteRequestSchema'} | ${KillProcessRouteRequestSchema} + ${'SuspendProcessRouteRequestSchema'} | ${SuspendProcessRouteRequestSchema} + `('$name', ({ name, killOrSuspendSchema }) => { it('should not accept when no endpoint_ids', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({}); + killOrSuspendSchema.body.validate({}); }).toThrow(); }); it('should not accept empty endpoint_ids array', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: [], }); }).toThrow(); @@ -524,7 +530,7 @@ describe('actions schemas', () => { it('should not accept empty string as endpoint id', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: [' '], }); }).toThrow(); @@ -532,7 +538,7 @@ describe('actions schemas', () => { it('should not accept any empty string in endpoint_ids array', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: ['x', ' ', 'y'], }); }).toThrow(); @@ -540,7 +546,7 @@ describe('actions schemas', () => { it('should accept pid', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: ['ABC-XYZ-000'], parameters: { pid: 1234, @@ -551,7 +557,7 @@ describe('actions schemas', () => { it('should accept entity_id', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: ['ABC-XYZ-000'], parameters: { entity_id: 'abc123', @@ -562,7 +568,7 @@ describe('actions schemas', () => { it('should reject pid and entity_id together', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: ['ABC-XYZ-000'], parameters: { pid: 1234, @@ -574,7 +580,7 @@ describe('actions schemas', () => { it('should reject if no pid or entity_id', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: ['ABC-XYZ-000'], comment: 'a user comment', parameters: {}, @@ -584,7 +590,7 @@ describe('actions schemas', () => { it('should accept a comment', () => { expect(() => { - KillOrSuspendProcessRequestSchema.body.validate({ + killOrSuspendSchema.body.validate({ endpoint_ids: ['ABC-XYZ-000'], comment: 'a user comment', parameters: { @@ -595,6 +601,41 @@ describe('actions schemas', () => { }); }); + describe('KillProcessRequestSchema for SentinelOne', () => { + it('should error if agentType is not sentinel_one and process_name parameter is used', () => { + expect(() => { + KillProcessRouteRequestSchema.body.validate({ + endpoint_ids: ['abc'], + parameters: { + process_name: 'explorer.exe', + }, + }); + }).toThrow(); + }); + + it('should error if agentType is sentinel_one but process_name is not defined', () => { + expect(() => { + KillProcessRouteRequestSchema.body.validate({ + endpoint_ids: ['abc'], + agent_type: 'sentinel_one', + parameters: { pid: 4 }, + }); + }).toThrow(); + }); + + it('should allow use of process_name if agentType is sentinel_one', () => { + expect(() => { + KillProcessRouteRequestSchema.body.validate({ + endpoint_ids: ['abc'], + agent_type: 'sentinel_one', + parameters: { + process_name: 'explorer.exe', + }, + }); + }).not.toThrow(); + }); + }); + describe('ExecuteActionRequestSchema', () => { it('should not accept when no endpoint_ids', () => { expect(() => { diff --git a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/is_response_action_supported.ts b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/is_response_action_supported.ts index 0c0d0db960709..61216afeea1ee 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/is_response_action_supported.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/is_response_action_supported.ts @@ -74,7 +74,7 @@ const RESPONSE_ACTIONS_SUPPORT_MAP: SupportMap = { }, manual: { endpoint: true, - sentinel_one: false, + sentinel_one: true, crowdstrike: false, }, }, diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index 28be6f8d3d139..01da8a39ee723 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -8,12 +8,14 @@ import type { TypeOf } from '@kbn/config-schema'; import type { EcsError } from '@elastic/ecs'; import type { BaseFileMetadata, FileCompression, FileJSON } from '@kbn/files-plugin/common'; -import type { ResponseActionBodySchema, UploadActionApiRequestBody } from '../../api/endpoint'; -import type { ActionStatusRequestSchema } from '../../api/endpoint/actions/action_status_route'; import type { - KillOrSuspendProcessRequestSchema, - NoParametersRequestSchema, -} from '../../api/endpoint/actions/common/base'; + ResponseActionBodySchema, + UploadActionApiRequestBody, + KillProcessRouteRequestSchema, + SuspendProcessRouteRequestSchema, +} from '../../api/endpoint'; +import type { ActionStatusRequestSchema } from '../../api/endpoint/actions/action_status_route'; +import type { NoParametersRequestSchema } from '../../api/endpoint/actions/common/base'; import type { ResponseActionAgentType, ResponseActionsApiCommandNames, @@ -178,19 +180,28 @@ export interface LogsEndpointActionResponse< meta?: TMeta; } -interface ResponseActionParametersWithPid { +export interface ResponseActionParametersWithPid { pid: number; entity_id?: never; + process_name?: never; } -interface ResponseActionParametersWithEntityId { +export interface ResponseActionParametersWithEntityId { pid?: never; + process_name?: never; entity_id: string; } -export type ResponseActionParametersWithPidOrEntityId = +export interface ResponseActionParametersWithProcessName { + pid?: never; + entity_id?: never; + process_name: string; +} + +export type ResponseActionParametersWithProcessData = | ResponseActionParametersWithPid - | ResponseActionParametersWithEntityId; + | ResponseActionParametersWithEntityId + | ResponseActionParametersWithProcessName; export interface ResponseActionGetFileParameters { path: string; @@ -207,7 +218,7 @@ export interface ResponseActionScanParameters { export type EndpointActionDataParameterTypes = | undefined - | ResponseActionParametersWithPidOrEntityId + | ResponseActionParametersWithProcessData | ResponseActionsExecuteParameters | ResponseActionGetFileParameters | ResponseActionUploadParameters @@ -350,7 +361,12 @@ export type HostIsolationRequestBody = TypeOf; -export type KillOrSuspendProcessRequestBody = TypeOf; +export type KillProcessRequestBody = TypeOf; + +export type SuspendProcessRequestBody = TypeOf; + +/** Note: this type should almost never be used. Use instead the response action specific types above */ +export type KillOrSuspendProcessRequestBody = KillProcessRequestBody & SuspendProcessRequestBody; export interface HostIsolationResponse { action: string; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/sentinel_one.ts b/x-pack/plugins/security_solution/common/endpoint/types/sentinel_one.ts index 91a06ffc5ffca..a15557617d9f0 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/sentinel_one.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/sentinel_one.ts @@ -115,3 +115,11 @@ export interface SentinelOneGetFileResponseMeta { createdAt: string; filename: string; } + +export interface SentinelOneKillProcessRequestMeta extends SentinelOneIsolationRequestMeta { + /** + * The Parent Task Is that is executing the kill process action in SentinelOne. + * Used to check on the status of that action + */ + parentTaskId: string; +} diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 66b5f4bd948a1..03e1d324494f4 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -81,6 +81,9 @@ export const allowedExperimentalValues = Object.freeze({ /** Enables the `get-file` response action for SentinelOne */ responseActionsSentinelOneGetFileEnabled: true, + /** Enables the `kill-process` response action for SentinelOne */ + responseActionsSentinelOneKillProcessEnabled: false, + /** * Enables the ability to send Response actions to Crowdstrike and persist the results * in ES. @@ -239,11 +242,6 @@ export const allowedExperimentalValues = Object.freeze({ */ unifiedManifestEnabled: true, - /** - * Enables Security AI Assistant's Flyout mode - */ - aiAssistantFlyoutMode: true, - /** * Enables the new modal for the value list items */ diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md index e2f98296e199c..beda8a9517830 100644 --- a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md @@ -55,14 +55,6 @@ Status: `in progress`. The current test plan matches `Milestone 2` of the [Rule - [**Scenario: User can see correct rule information in preview before upgrading**](#scenario-user-can-see-correct-rule-information-in-preview-before-upgrading) - [**Scenario: Tabs and sections without content should be hidden in preview before upgrading**](#scenario-tabs-and-sections-without-content-should-be-hidden-in-preview-before-upgrading) - [Rule upgrade workflow: filtering, sorting, pagination](#rule-upgrade-workflow-filtering-sorting-pagination) - - [Rule upgrade workflow: simple diff algorithm](#rule-upgrade-workflow-simple-diff-algorithm) - - [**Scenario: Rule field doesn't have an update and has no custom value**](#scenario-rule-field-doesnt-have-an-update-and-has-no-custom-value) - - [**Scenario: Rule field doesn't have an update but has a custom value**](#scenario-rule-field-doesnt-have-an-update-but-has-a-custom-value) - - [**Scenario: Rule field has an update and doesn't have a custom value**](#scenario-rule-field-has-an-update-and-doesnt-have-a-custom-value) - - [**Scenario: Rule field has an update and a custom value that are the same**](#scenario-rule-field-has-an-update-and-a-custom-value-that-are-the-same) - - [**Scenario: Rule field has an update and a custom value that are NOT the same**](#scenario-rule-field-has-an-update-and-a-custom-value-that-are-not-the-same) - - [**Scenario: Rule field has an update and a custom value that are NOT the same and the rule base version doesn't exist**](#scenario-rule-field-has-an-update-and-a-custom-value-that-are-not-the-same-and-the-rule-base-version-doesnt-exist) - - [**Scenario: Rule field has an update and a custom value that are the same and the rule base version doesn't exist**](#scenario-rule-field-has-an-update-and-a-custom-value-that-are-the-same-and-the-rule-base-version-doesnt-exist) - [Rule upgrade workflow: viewing rule changes in JSON diff view](#rule-upgrade-workflow-viewing-rule-changes-in-json-diff-view) - [**Scenario: User can see changes in a side-by-side JSON diff view**](#scenario-user-can-see-changes-in-a-side-by-side-json-diff-view) - [**Scenario: User can see precisely how property values would change after upgrade**](#scenario-user-can-see-precisely-how-property-values-would-change-after-upgrade) @@ -823,144 +815,6 @@ And the Investigation Guide tab should NOT be displayed TODO: add scenarios https://github.com/elastic/kibana/issues/166215 -### Rule upgrade workflow: simple diff algorithm - -#### **Scenario: Rule field doesn't have an update and has no custom value** - -**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithm - -```Gherkin -Given at least 1 prebuilt rule is installed in Kibana -And for this rule there is a new version available -And field is not customized by the user (current version == base version) -And field is not updated by Elastic in this upgrade (target version == base version) -Then for field the diff algorithm should output the current version as the merged one without a conflict -And field should not be returned from the `upgrade/_review` API endpoint -And field should not be shown in the upgrade preview UI - -Examples: -| field_name | base_version | current_version | target_version | -| name | "A" | "A" | "A" | -| risk_score | 1 | 1 | 1 | -``` - -#### **Scenario: Rule field doesn't have an update but has a custom value** - -**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithm - -```Gherkin -Given at least 1 prebuilt rule is installed in Kibana -And for this rule there is a new version available -And field is customized by the user (current version != base version) -And field is not updated by Elastic in this upgrade (target version == base version) -Then for field the diff algorithm should output the current version as the merged one without a conflict -And field should be returned from the `upgrade/_review` API endpoint -And field should be shown in the upgrade preview UI - -Examples: -| field_name | base_version | current_version | target_version | -| name | "A" | "B" | "A" | -| risk_score | 1 | 2 | 1 | -``` - -#### **Scenario: Rule field has an update and doesn't have a custom value** - -**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithm - -```Gherkin -Given at least 1 prebuilt rule is installed in Kibana -And for this rule there is a new version available -And field is not customized by the user (current version == base version) -And field is updated by Elastic in this upgrade (target version != base version) -Then for field the diff algorithm should output the target version as the merged one without a conflict -And field should be returned from the `upgrade/_review` API endpoint -And field should be shown in the upgrade preview UI - -Examples: -| field_name | base_version | current_version | target_version | -| name | "A" | "A" | "B" | -| risk_score | 1 | 1 | 2 | -``` - -#### **Scenario: Rule field has an update and a custom value that are the same** - -**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithm - -```Gherkin -Given at least 1 prebuilt rule is installed in Kibana -And for this rule there is a new version available -And field is customized by the user (current version != base version) -And field is updated by Elastic in this upgrade (target version != base version) -And customized field is the same as the Elastic update in this upgrade (current version == target version) -Then for field the diff algorithm should output the current version as the merged one without a conflict -And field should be returned from the `upgrade/_review` API endpoint -And field should be shown in the upgrade preview UI - -Examples: -| field_name | base_version | current_version | target_version | -| name | "A" | "B" | "B" | -| risk_score | 1 | 2 | 2 | -``` - -#### **Scenario: Rule field has an update and a custom value that are NOT the same** - -**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithm - -```Gherkin -Given at least 1 prebuilt rule is installed in Kibana -And for this rule there is a new version available -And field is customized by the user (current version != base version) -And field is updated by Elastic in this upgrade (target version != base version) -And customized field is different than the Elastic update in this upgrade (current version != target version) -Then for field the diff algorithm should output the current version as the merged one with a conflict -And field should be returned from the `upgrade/_review` API endpoint -And field should be shown in the upgrade preview UI - -Examples: -| field_name | base_version | current_version | target_version | -| name | "A" | "B" | "C" | -| risk_score | 1 | 2 | 3 | -``` - -#### **Scenario: Rule field has an update and a custom value that are NOT the same and the rule base version doesn't exist** - -**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithm - -```Gherkin -Given at least 1 prebuilt rule is installed in Kibana -And for this rule there is a new version available -And the base version of the rule cannot be determined -And customized field is different than the Elastic update in this upgrade (current version != target version) -Then for field the diff algorithm should output the target version as the merged one with a conflict -And field should be returned from the `upgrade/_review` API endpoint -And field should be shown in the upgrade preview UI - -Examples: -| field_name | base_version | current_version | target_version | -| name | N/A | "B" | "C" | -| risk_score | N/A | 2 | 3 | -``` - -#### **Scenario: Rule field has an update and a custom value that are the same and the rule base version doesn't exist** - -**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithm - -```Gherkin -Given at least 1 prebuilt rule is installed in Kibana -And for this rule there is a new version available -And the base version of the rule cannot be determined -And customized field is the same as the Elastic update in this upgrade (current version == target version) -Then for field the diff algorithm should output the current version as the merged one without a conflict -And field should not be returned from the `upgrade/_review` API endpoint -And field should not be shown in the upgrade preview UI - - -Examples: -| field_name | base_version | current_version | target_version | -| name | N/A | "A" | "A" | -| risk_score | N/A | 1 | 1 | -``` - ### Rule upgrade workflow: viewing rule changes in JSON diff view #### **Scenario: User can see changes in a side-by-side JSON diff view** diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/upgrade_review_algorithms.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/upgrade_review_algorithms.md new file mode 100644 index 0000000000000..fad01b9698b9b --- /dev/null +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/upgrade_review_algorithms.md @@ -0,0 +1,246 @@ +# Diff Algorithms for `upgrade/_review` Endpoint + +This is a test plan for the `upgrade/_review` endpoint diff algorithms that are a part of the larger prebuilt rules customization feature. These algorithms determine what fields get returned when a user makes an API request to review changes as a part of the rule update process and determine what version of those fields should be displayed by the UI. + +Status: `in progress`. + +## Table of Contents + +- [Useful information](#useful-information) + - [Tickets](#tickets) + - [Terminology](#terminology) + - [Assumptions](#assumptions) + - [Non-functional requirements](#non-functional-requirements) + - [Functional requirements](#functional-requirements) +- [Scenarios](#scenarios) + + - [Rule field doesn't have an update and has no custom value - `AAA`](#rule-field-doesnt-have-an-update-and-has-no-custom-value---aaa) + - [**Scenario: `AAA` - Rule field is any type**](#scenario-aaa---rule-field-is-any-type) + - [Rule field doesn't have an update but has a custom value - `ABA`](#rule-field-doesnt-have-an-update-but-has-a-custom-value---aba) + - [**Scenario: `ABA` - Rule field is any type**](#scenario-aba---rule-field-is-any-type) + - [Rule field has an update and doesn't have a custom value - `AAB`](#rule-field-has-an-update-and-doesnt-have-a-custom-value---aab) + - [**Scenario: `AAB` - Rule field is any type**](#scenario-aab---rule-field-is-any-type) + - [Rule field has an update and a custom value that are the same - `ABB`](#rule-field-has-an-update-and-a-custom-value-that-are-the-same---abb) + - [**Scenario: `ABB` - Rule field is any type**](#scenario-abb---rule-field-is-any-type) + - [Rule field has an update and a custom value that are NOT the same - `ABC`](#rule-field-has-an-update-and-a-custom-value-that-are-not-the-same---abc) + - [**Scenario: `ABC` - Rule field is a number or single line string**](#scenario-abc---rule-field-is-a-number-or-single-line-string) + - [**Scenario: `ABC` - Rule field is an array of scalar values**](#scenario-abc---rule-field-is-an-array-of-scalar-values) + - [Rule field has an update and a custom value that are the same and the rule base version doesn't exist - `-AA`](#rule-field-has-an-update-and-a-custom-value-that-are-the-same-and-the-rule-base-version-doesnt-exist----aa) + - [**Scenario: `-AA` - Rule field is any type**](#scenario--aa---rule-field-is-any-type) + - [Rule field has an update and a custom value that are NOT the same and the rule base version doesn't exist - `-BC`](#rule-field-has-an-update-and-a-custom-value-that-are-not-the-same-and-the-rule-base-version-doesnt-exist----bc) + - [**Scenario: `-BC` - Rule field is a number or single line string**](#scenario--bc---rule-field-is-a-number-or-single-line-string) + - [**Scenario: `-BC` - Rule field is an array of scalar values**](#scenario--bc---rule-field-is-an-array-of-scalar-values) + +## Useful information + +### Tickets + +- [Users can customize prebuilt detection rules](https://github.com/elastic/kibana/issues/174168) epic +- [Implement single-line string diff algorithm](https://github.com/elastic/kibana/issues/180158) +- [Implement number diff algorithm](https://github.com/elastic/kibana/issues/180160) +- [Implement array of scalar values diff algorithm](https://github.com/elastic/kibana/issues/180162) + +### Terminology + +- **Base version**: Also labeled as `base_version`. This is the version of a rule authored by Elastic as it is installed from the `security_detection_engine` package, with no customizations to any fields by the user. + +- **Current version**: Also labeled as `current_version`. This is the version of the rule that the user currently has installed. Consists of the `base_version` of the rules plus all customization applies to its fields by the user. + +- **Target version**: Also labeled as `target_version`. This is the version of the rule that contains the update from Elastic. + +- **Merged version**: Also labeled as `merged_version`. This is the version of the rule that we determine via the various algorithms. It could contain a mix of all the rule versions on a per-field basis to create a singluar version of the rule containing all relevant updates and user changes to display to the user. + +### Assumptions + +- All scenarios will contain at least 1 prebuilt rule installed in Kibana. +- A new version will be available for rule(s). + +## Scenarios + +### Rule field doesn't have an update and has no custom value - `AAA` + +#### **Scenario: `AAA` - Rule field is any type** + +**Automation**: 3 integration tests with mock rules + a set of unit tests for each algorithm + +```Gherkin +Given field is not customized by the user (current version == base version) +And field is not updated by Elastic in this upgrade (target version == base version) +Then for field the diff algorithm should output the current version as the merged one without a conflict +And field should not be returned from the `upgrade/_review` API endpoint +And field should not be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| single line string | name | "A" | "A" | "A" | "A" | +| number | risk_score | 1 | 1 | 1 | 1 | +| array of scalars | tags | ["one", "two", "three"] | ["one", "three", "two"] | ["three", "one", "two"] | ["one", "three", "two"] | +``` + +### Rule field doesn't have an update but has a custom value - `ABA` + +#### **Scenario: `ABA` - Rule field is any type** + +**Automation**: 3 integration tests with mock rules + a set of unit tests for each algorithm + +```Gherkin +Given field is customized by the user (current version != base version) +And field is not updated by Elastic in this upgrade (target version == base version) +Then for field the diff algorithm should output the current version as the merged one without a conflict +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| single line string | name | "A" | "B" | "A" | "B" | +| number | risk_score | 1 | 2 | 1 | 2 | +| array of scalars | tags | ["one", "two", "three"] | ["one", "two", "four"] | ["one", "two", "three"] | ["one", "two", "four"] | +``` + +### Rule field has an update and doesn't have a custom value - `AAB` + +#### **Scenario: `AAB` - Rule field is any type** + +**Automation**: 3 integration tests with mock rules + a set of unit tests for each algorithm + +```Gherkin +Given field is not customized by the user (current version == base version) +And field is updated by Elastic in this upgrade (target version != base version) +Then for field the diff algorithm should output the target version as the merged one without a conflict +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| single line string | name | "A" | "A" | "B" | "B" | +| number | risk_score | 1 | 1 | 2 | 2 | +| array of scalars | tags | ["one", "two", "three"] | ["one", "two", "three"] | ["one", "two", "four"] | ["one", "two", "four"] | +``` + +### Rule field has an update and a custom value that are the same - `ABB` + +#### **Scenario: `ABB` - Rule field is any type** + +**Automation**: 3 integration tests with mock rules + a set of unit tests for each algorithm + +```Gherkin +Given field is customized by the user (current version != base version) +And field is updated by Elastic in this upgrade (target version != base version) +And customized field is the same as the Elastic update in this upgrade (current version == target version) +Then for field the diff algorithm should output the current version as the merged one without a conflict +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| single line string | name | "A" | "B" | "B" | "B" | +| number | risk_score | 1 | 2 | 2 | 2 | +| array of scalars | tags | ["one", "two", "three"] | ["one", "two", "four"] | ["one", "two", "four"] | ["one", "two", "four"] | +``` + +### Rule field has an update and a custom value that are NOT the same - `ABC` + +#### **Scenario: `ABC` - Rule field is a number or single line string** + +**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithms + +```Gherkin +Given field is customized by the user (current version != base version) +And field is updated by Elastic in this upgrade (target version != base version) +And customized field is different than the Elastic update in this upgrade (current version != target version) +Then for field the diff algorithm should output the current version as the merged one with a non-solvable conflict +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| single line string | name | "A" | "B" | "C" | "B" | +| number | risk_score | 1 | 2 | 3 | 2 | +``` + +#### **Scenario: `ABC` - Rule field is an array of scalar values** + +**Automation**: 5 integration tests with mock rules + a set of unit tests for the algorithm + +```Gherkin +Given field is customized by the user (current version != base version) +And field is updated by Elastic in this upgrade (target version != base version) +And customized field is different than the Elastic update in this upgrade (current version != target version) +Then for field the diff algorithm should output a custom merged version with a solvable conflict +And arrays should be deduplicated before comparison +And arrays should be compared sensitive of case +And arrays should be compared agnostic of order +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| array of scalars | tags | ["one", "two", "three"] | ["one", "two", "four"] | ["one", "two", "five"] | ["one", "two", "four", "five"] | +| array of scalars | tags | ["one", "two", "three"] | ["two", "one"] | ["one", "four"] | ["one", "four"] | +| array of scalars | tags | ["one", "two", "three"] | [] | ["one", "two", "five"] | ["five"] | +| array of scalars | tags | ["one", "two", "two"] | ["two", "one", "three"] | ["three", "three", "one"] | ["one", "three"] | +| array of scalars | index | ["logs-*", "endgame-*", "endpoint-*"] | ["Logs-*", "endgame-*"] | ["logs-*", "endgame-*", "new-*"] | ["Logs-*", "endgame-*", "new-*"] | +| array of scalars | index | ["logs-*"] | ["logs-*", "Logs-*"] | ["logs-*", "new-*"] | ["logs-*", "Logs-*", "new-*"] | +``` + +### Rule field has an update and a custom value that are the same and the rule base version doesn't exist - `-AA` + +#### **Scenario: `-AA` - Rule field is any type** + +**Automation**: 3 integration tests with mock rules + a set of unit tests for each algorithm + +```Gherkin +Given at least 1 installed prebuilt rule has a new version available +And the base version of the rule cannot be determined +And customized field is the same as the Elastic update in this upgrade (current version == target version) +Then for field the diff algorithm should output the current version as the merged one without a conflict +And field should not be returned from the `upgrade/_review` API endpoint +And field should not be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| single line string | name | N/A | "A" | "A" | "A" | +| number | risk_score | N/A | 1 | 1 | 1 | +| array of scalars | tags | N/A | ["one", "three", "two"] | ["three", "one", "two"] | ["one", "three", "two"] | +``` + +### Rule field has an update and a custom value that are NOT the same and the rule base version doesn't exist - `-BC` + +#### **Scenario: `-BC` - Rule field is a number or single line string** + +**Automation**: 2 integration tests with mock rules + a set of unit tests for the algorithms + +```Gherkin +Given at least 1 installed prebuilt rule has a new version available +And the base version of the rule cannot be determined +And customized field is different than the Elastic update in this upgrade (current version != target version) +Then for field the diff algorithm should output the target version as the merged one with a solvable conflict +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| single line string | name | N/A | "B" | "C" | "C" | +| number | risk_score | N/A | 2 | 3 | 3 | +``` + +#### **Scenario: `-BC` - Rule field is an array of scalar values** + +**Automation**: 1 integration test with mock rules + a set of unit tests for the algorithm + +```Gherkin +Given field is customized by the user (current version != base version) +And field is updated by Elastic in this upgrade (target version != base version) +And customized field is different than the Elastic update in this upgrade (current version != target version) +Then for field the diff algorithm should output a custom merged version with a solvable conflict +And arrays should be deduplicated before comparison +And arrays should be compared sensitive of case +And arrays should be compared agnostic of order +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI + + +Examples: +| algorithm | field_name | base_version | current_version | target_version | merged_version | +| array of scalars | tags | N/A | ["one", "two", "four"] | ["one", "two", "five"] | ["one", "two", "four", "five"] | +``` diff --git a/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx b/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx index df961ac223c4d..baf36e278b71f 100644 --- a/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx @@ -23,16 +23,15 @@ import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experime interface Props { message: ClientMessage; - isFlyoutMode: boolean; } -const CommentActionsComponent: React.FC = ({ message, isFlyoutMode }) => { +const CommentActionsComponent: React.FC = ({ message }) => { const toasts = useToasts(); const { cases } = useKibana().services; const dispatch = useDispatch(); const isModelEvaluationEnabled = useIsExperimentalFeatureEnabled('assistantModelEvaluation'); - const { showAssistantOverlay, traceOptions } = useAssistantContext(); + const { traceOptions } = useAssistantContext(); const associateNote = useCallback( (noteId: string) => dispatch(timelineActions.addNote({ id: TimelineId.active, noteId })), @@ -65,10 +64,6 @@ const CommentActionsComponent: React.FC = ({ message, isFlyoutMode }) => }); const onAddToExistingCase = useCallback(() => { - if (!isFlyoutMode) { - showAssistantOverlay({ showOverlay: false }); - } - selectCaseModal.open({ getAttachments: () => [ { @@ -78,7 +73,7 @@ const CommentActionsComponent: React.FC = ({ message, isFlyoutMode }) => }, ], }); - }, [content, isFlyoutMode, selectCaseModal, showAssistantOverlay]); + }, [content, selectCaseModal]); // Note: This feature is behind the `isModelEvaluationEnabled` FF. If ever released, this URL should be configurable // as APM data may not go to the same cluster where the Kibana instance is running diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx index b59f14684a2ca..884af527f4be0 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/index.test.tsx @@ -39,7 +39,6 @@ const testProps = { isFetchingResponse: false, currentConversation, showAnonymizedValues, - isFlyoutMode: false, }; describe('getComments', () => { it('Does not add error state message has no error', () => { diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx index 10d5a15c800ae..8976c851c6e65 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/index.tsx @@ -60,7 +60,6 @@ export const getComments = ({ refetchCurrentConversation, regenerateMessage, showAnonymizedValues, - isFlyoutMode, currentUserAvatar, setIsStreaming, }: { @@ -71,7 +70,6 @@ export const getComments = ({ refetchCurrentConversation: () => void; regenerateMessage: (conversationId: string) => void; showAnonymizedValues: boolean; - isFlyoutMode: boolean; currentUserAvatar?: UserAvatar; setIsStreaming: (isStreaming: boolean) => void; }): EuiCommentProps[] => { @@ -187,7 +185,7 @@ export const getComments = ({ return { ...messageProps, - actions: , + actions: , children: ( { @@ -31,16 +30,10 @@ export const AssistantOverlay: React.FC = () => { }); const { assistantAvailability } = useAssistantContext(); - const aiAssistantFlyoutMode = useIsExperimentalFeatureEnabled('aiAssistantFlyoutMode'); if (!assistantAvailability.hasAssistantPrivilege) { return null; } - return ( - - ); + return ; }; diff --git a/x-pack/plugins/security_solution/public/assistant/stack_management/management_settings.tsx b/x-pack/plugins/security_solution/public/assistant/stack_management/management_settings.tsx index 525315ca36eb2..31f3910fdab49 100644 --- a/x-pack/plugins/security_solution/public/assistant/stack_management/management_settings.tsx +++ b/x-pack/plugins/security_solution/public/assistant/stack_management/management_settings.tsx @@ -16,13 +16,10 @@ import { } from '@kbn/elastic-assistant'; import { useConversation } from '@kbn/elastic-assistant/impl/assistant/use_conversation'; import type { FetchConversationsResponse } from '@kbn/elastic-assistant/impl/assistant/api'; -import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; const defaultSelectedConversationId = WELCOME_CONVERSATION_TITLE; export const ManagementSettings = React.memo(() => { - const isFlyoutMode = useIsExperimentalFeatureEnabled('aiAssistantFlyoutMode'); - const { baseConversations, http, @@ -49,8 +46,8 @@ export const ManagementSettings = React.memo(() => { const currentConversation = useMemo( () => conversations?.[defaultSelectedConversationId] ?? - getDefaultConversation({ cTitle: WELCOME_CONVERSATION_TITLE, isFlyoutMode }), - [conversations, getDefaultConversation, isFlyoutMode] + getDefaultConversation({ cTitle: WELCOME_CONVERSATION_TITLE }), + [conversations, getDefaultConversation] ); if (conversations) { @@ -58,7 +55,6 @@ export const ManagementSettings = React.memo(() => { diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.test.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.test.tsx index 938da7f930d51..4f242cedfc92e 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.test.tsx @@ -38,7 +38,7 @@ describe('Header', () => { ); - const connectorSelector = screen.getByTestId('connectorSelectorPlaceholderButton'); + const connectorSelector = screen.getByTestId('addNewConnectorButton'); expect(connectorSelector).toBeInTheDocument(); }); @@ -61,7 +61,7 @@ describe('Header', () => { ); - const connectorSelector = screen.queryByTestId('connectorSelectorPlaceholderButton'); + const connectorSelector = screen.queryByTestId('addNewConnectorButton'); expect(connectorSelector).not.toBeInTheDocument(); }); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.tsx index 78ad8db6d2f6e..fa4a9caa3dcb1 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.tsx @@ -38,7 +38,6 @@ const HeaderComponent: React.FC = ({ onCancel, stats, }) => { - const isFlyoutMode = false; // always false for attack discovery const { hasAssistantPrivilege } = useAssistantAvailability(); const { euiTheme } = useEuiTheme(); const disabled = !hasAssistantPrivilege || connectorId == null; @@ -85,7 +84,6 @@ const HeaderComponent: React.FC = ({ {connectorsAreConfigured && ( { expect(bodyText).toHaveTextContent(FIRST_SET_UP); }); - - it('renders connector prompt', () => { - const connectorPrompt = screen.getByTestId('prompt'); - - expect(connectorPrompt).toBeInTheDocument(); - }); }); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/welcome/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/welcome/index.tsx index 3f8488f6f64b4..7ab90b524bb93 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/welcome/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/welcome/index.tsx @@ -6,21 +6,13 @@ */ import { AssistantAvatar } from '@kbn/elastic-assistant'; -import { useConnectorSetup } from '@kbn/elastic-assistant/impl/connectorland/connector_setup'; +import { ConnectorSetup } from '@kbn/elastic-assistant/impl/connectorland/connector_setup'; import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { noop } from 'lodash/fp'; import * as i18n from './translations'; const WelcomeComponent: React.FC = () => { - const { prompt: connectorPrompt } = useConnectorSetup({ - isFlyoutMode: true, // prevents the "Click to skip" button from showing - onConversationUpdate: async () => {}, - onSetupComplete: noop, // this callback cannot be used to select a connector, so it's not used - updateConversationsOnSaveConnector: false, // no conversation to update - }); - const title = useMemo( () => ( { - {connectorPrompt} + + {}} + updateConversationsOnSaveConnector={false} // no conversation to update + /> + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx index af878acd8b104..3f32ba4fd5a00 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx @@ -18,7 +18,7 @@ import type { TimelineEventsDetailsItem, RiskSeverity, } from '../../../../../common/search_strategy'; -import { RiskSummary } from '../../../../entity_analytics/components/risk_summary'; +import { RiskSummaryPanel } from '../../../../entity_analytics/components/risk_summary_panel'; import { EnrichmentSummary } from './enrichment_summary'; import { RiskScoreEntity } from '../../../../../common/search_strategy'; import { useHasSecurityCapability } from '../../../../helper_hooks'; @@ -137,7 +137,7 @@ const ThreatSummaryViewComponent: React.FC<{ {hasEntityAnalyticsCapability && ( <> - - => { +export const killProcess = (params: KillProcessRequestBody): Promise => { return KibanaServices.get().http.post(KILL_PROCESS_ROUTE, { body: JSON.stringify(params), version: '2023-10-31', @@ -24,7 +23,7 @@ export const killProcess = ( /** Suspends a process specified by pid or entity id on a host running Endpoint Security */ export const suspendProcess = ( - params: KillOrSuspendProcessRequestBody + params: SuspendProcessRequestBody ): Promise => { return KibanaServices.get().http.post(SUSPEND_PROCESS_ROUTE, { body: JSON.stringify(params), diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/constants.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/constants.ts index 45cb8f0633e65..0672e599160ad 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/constants.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/constants.ts @@ -51,7 +51,6 @@ export const DEFINITION_UPGRADE_FIELD_ORDER: Array = [ 'threat_indicator_path', 'concurrent_searches', 'items_per_search', - 'alert_suppression', 'new_terms_fields', 'history_window_start', 'max_signals', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_diff/get_field_diffs_for_grouped_fields.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_diff/get_field_diffs_for_grouped_fields.ts index 2c5c20da928dd..21717998483ea 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_diff/get_field_diffs_for_grouped_fields.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_diff/get_field_diffs_for_grouped_fields.ts @@ -8,6 +8,9 @@ import stringify from 'json-stable-stringify'; import type { AllFieldsDiff, + RuleFieldsDiffWithDataSource, + RuleFieldsDiffWithEqlQuery, + RuleFieldsDiffWithEsqlQuery, RuleFieldsDiffWithKqlQuery, } from '../../../../../../common/api/detection_engine'; import type { FieldDiff } from '../../../model/rule_details/rule_field_diff'; @@ -24,7 +27,7 @@ export const sortAndStringifyJson = (fieldValue: unknown): string => { }; export const getFieldDiffsForDataSource = ( - dataSourceThreeWayDiff: AllFieldsDiff['data_source'] + dataSourceThreeWayDiff: RuleFieldsDiffWithDataSource['data_source'] ): FieldDiff[] => { const currentType = sortAndStringifyJson(dataSourceThreeWayDiff.current_version?.type); const targetType = sortAndStringifyJson(dataSourceThreeWayDiff.target_version?.type); @@ -171,7 +174,9 @@ export const getFieldDiffsForKqlQuery = ( ]; }; -export const getFieldDiffsForEqlQuery = (eqlQuery: AllFieldsDiff['eql_query']): FieldDiff[] => { +export const getFieldDiffsForEqlQuery = ( + eqlQuery: RuleFieldsDiffWithEqlQuery['eql_query'] +): FieldDiff[] => { const currentQuery = sortAndStringifyJson(eqlQuery.current_version?.query); const targetQuery = sortAndStringifyJson(eqlQuery.target_version?.query); @@ -199,7 +204,9 @@ export const getFieldDiffsForEqlQuery = (eqlQuery: AllFieldsDiff['eql_query']): ]; }; -export const getFieldDiffsForEsqlQuery = (esqlQuery: AllFieldsDiff['esql_query']): FieldDiff[] => { +export const getFieldDiffsForEsqlQuery = ( + esqlQuery: RuleFieldsDiffWithEsqlQuery['esql_query'] +): FieldDiff[] => { const currentQuery = sortAndStringifyJson(esqlQuery.current_version?.query); const targetQuery = sortAndStringifyJson(esqlQuery.target_version?.query); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_diff_tab.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_diff_tab.tsx index b22fc1b73bbe6..dd6d0417d3bb6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_diff_tab.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_diff_tab.tsx @@ -28,7 +28,7 @@ import * as i18n from './json_diff/translations'; import { getHumanizedDuration } from '../../../../detections/pages/detection_engine/rules/helpers'; /* Inclding these properties in diff display might be confusing to users. */ -const HIDDEN_PROPERTIES = [ +const HIDDEN_PROPERTIES: Array = [ /* By default, prebuilt rules don't have any actions or exception lists. So if a user has defined actions or exception lists for a rule, it'll show up as diff. This looks confusing as the user might think that their actions and exceptions lists will get removed after the upgrade, which is not the case - they will be preserved. */ @@ -44,13 +44,13 @@ const HIDDEN_PROPERTIES = [ 'revision', /* - "updated_at" value is regenerated on every '/upgrade/_review' endpoint run + "updated_at" value is regenerated on every '/upgrade/_review' endpoint run and will therefore always show a diff. It adds no value to display it to the user. */ 'updated_at', /* - These values make sense only for installed prebuilt rules. + These values make sense only for installed prebuilt rules. They are not present in the prebuilt rule package. So, showing them in the diff doesn't add value. */ @@ -76,11 +76,11 @@ const normalizeRule = (originalRule: RuleResponse): RuleResponse => { const rule = { ...originalRule }; /* - Convert the "from" property value to a humanized duration string, like 'now-1m' or 'now-2h'. - Conversion is needed to skip showing the diff for the "from" property when the same - duration is represented in different time units. For instance, 'now-1h' and 'now-3600s' + Convert the "from" property value to a humanized duration string, like 'now-1m' or 'now-2h'. + Conversion is needed to skip showing the diff for the "from" property when the same + duration is represented in different time units. For instance, 'now-1h' and 'now-3600s' indicate a one-hour duration. - The same helper is used in the rule editing UI to format "from" before submitting the edits. + The same helper is used in the rule editing UI to format "from" before submitting the edits. So, after the rule is saved, the "from" property unit/value might differ from what's in the package. */ rule.from = formatScheduleStepData({ @@ -91,8 +91,8 @@ const normalizeRule = (originalRule: RuleResponse): RuleResponse => { /* Default "note" to an empty string if it's not present. - Sometimes, in a new version of a rule, the "note" value equals an empty string, while - in the old version, it wasn't specified at all (undefined becomes ''). In this case, + Sometimes, in a new version of a rule, the "note" value equals an empty string, while + in the old version, it wasn't specified at all (undefined becomes ''). In this case, it doesn't make sense to show diff, so we default falsy values to ''. */ rule.note = rule.note ?? ''; @@ -104,9 +104,9 @@ const normalizeRule = (originalRule: RuleResponse): RuleResponse => { rule.threat = filterEmptyThreats(rule.threat); /* - The "machine_learning_job_id" property is converted from the legacy string format - to the new array format during installation and upgrade. Thus, all installed rules - use the new format. For correct comparison, we must ensure that the rule update is + The "machine_learning_job_id" property is converted from the legacy string format + to the new array format during installation and upgrade. Thus, all installed rules + use the new format. For correct comparison, we must ensure that the rule update is also in the new format before showing the diff. */ if ('machine_learning_job_id' in rule) { @@ -115,7 +115,7 @@ const normalizeRule = (originalRule: RuleResponse): RuleResponse => { /* Default the "alias" property to null for all threat filters that don't have it. - Setting a default is needed to match the behavior of the rule editing UI, + Setting a default is needed to match the behavior of the rule editing UI, which also defaults the "alias" property to null. */ if (rule.type === 'threat_match' && Array.isArray(rule.threat_filters)) { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/index.tsx index 994158104df5b..93fce26b3c475 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/index.tsx @@ -11,7 +11,7 @@ import { useCheckSignalIndex } from '../../../detections/containers/detection_en import type { inputsModel } from '../../../common/store'; import { RiskScoreHeaderTitle } from '../risk_score_onboarding/risk_score_header_title'; import { HeaderSection } from '../../../common/components/header_section'; -import { RiskScoreDocLink } from '../risk_score_onboarding/risk_score_doc_link'; +import { EntityAnalyticsLearnMoreLink } from '../risk_score_onboarding/entity_analytics_doc_link'; import { RiskScoreEnableButton } from '../risk_score_onboarding/risk_score_enable_button'; import * as i18n from './translations'; @@ -55,7 +55,7 @@ const EnableRiskScoreComponent = ({ <> {text.body} {` `} - + } actions={ diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/translations.ts index 5816accea220a..d87f0a8b899fc 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/translations.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/translations.ts @@ -25,14 +25,6 @@ export const VIEW_ALL = i18n.translate( } ); -export const LEARN_MORE = (riskEntity?: RiskScoreEntity) => - i18n.translate('xpack.securitySolution.entityAnalytics.riskDashboard.learnMore', { - defaultMessage: 'Learn more about {riskEntity} risk', - values: { - riskEntity: getRiskEntityTranslation(riskEntity, true), - }, - }); - export const LAST_UPDATED = i18n.translate( 'xpack.securitySolution.entityAnalytics.riskDashboard.lastUpdatedTitle', { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/index.tsx index 4c92512e13344..c863904da6c66 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/index.tsx @@ -14,7 +14,7 @@ import { RiskInputsTab } from './tabs/risk_inputs/risk_inputs_tab'; export const RISK_INPUTS_TAB_TEST_ID = `${PREFIX}RiskInputsTab` as const; -export const getRiskInputTab = ({ entityType, entityName }: RiskInputsTabProps) => ({ +export const getRiskInputTab = ({ entityType, entityName, scopeId }: RiskInputsTabProps) => ({ id: EntityDetailsLeftPanelTab.RISK_INPUTS, 'data-test-subj': RISK_INPUTS_TAB_TEST_ID, name: ( @@ -23,5 +23,5 @@ export const getRiskInputTab = ({ entityType, entityName }: RiskInputsTabProps) defaultMessage="Risk contributions" /> ), - content: , + content: , }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx index 8524a2a6a26ea..d6a247f6558a7 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx @@ -9,7 +9,7 @@ import { render } from '@testing-library/react'; import React from 'react'; import { TestProviders } from '../../../../../common/mock'; import { times } from 'lodash/fp'; -import { RiskInputsTab } from './risk_inputs_tab'; +import { EXPAND_ALERT_TEST_ID, RiskInputsTab } from './risk_inputs_tab'; import { alertInputDataMock } from '../../mocks'; import { RiskSeverity } from '../../../../../../common/search_strategy'; import { RiskScoreEntity } from '../../../../../../common/entity_analytics/risk_engine'; @@ -49,6 +49,12 @@ const riskScore = { }, }; +const mockUseIsExperimentalFeatureEnabled = jest.fn().mockReturnValue(false); + +jest.mock('../../../../../common/hooks/use_experimental_features', () => ({ + useIsExperimentalFeatureEnabled: () => mockUseIsExperimentalFeatureEnabled(), +})); + const riskScoreWithAssetCriticalityContribution = (contribution: number) => { const score = JSON.parse(JSON.stringify(riskScore)); score.user.risk.category_2_score = contribution; @@ -74,7 +80,7 @@ describe('RiskInputsTab', () => { const { getByTestId, queryByTestId } = render( - + ); @@ -87,7 +93,7 @@ describe('RiskInputsTab', () => { const { queryByTestId } = render( - + ); @@ -116,13 +122,57 @@ describe('RiskInputsTab', () => { const { queryByTestId } = render( - + ); expect(queryByTestId('risk-input-contexts-title')).toBeInTheDocument(); }); + it('it renders alert preview button when feature flag is enable', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); + mockUseRiskScore.mockReturnValue({ + loading: false, + error: false, + data: [riskScore], + }); + mockUseRiskContributingAlerts.mockReturnValue({ + loading: false, + error: false, + data: [alertInputDataMock], + }); + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(EXPAND_ALERT_TEST_ID)).toBeInTheDocument(); + }); + + it('it does not render alert preview button when feature flag is disable', () => { + mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); + mockUseRiskScore.mockReturnValue({ + loading: false, + error: false, + data: [riskScore], + }); + mockUseRiskContributingAlerts.mockReturnValue({ + loading: false, + error: false, + data: [alertInputDataMock], + }); + + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId(EXPAND_ALERT_TEST_ID)).not.toBeInTheDocument(); + }); + it('Displays 0.00 for the asset criticality contribution if the contribution value is less than -0.01', () => { mockUseUiSetting.mockReturnValue([true]); @@ -134,7 +184,7 @@ describe('RiskInputsTab', () => { const { getByTestId } = render( - + ); const contextsTable = getByTestId('risk-input-contexts-table'); @@ -153,7 +203,7 @@ describe('RiskInputsTab', () => { const { getByTestId } = render( - + ); const contextsTable = getByTestId('risk-input-contexts-table'); @@ -172,7 +222,7 @@ describe('RiskInputsTab', () => { const { getByTestId } = render( - + ); @@ -201,7 +251,7 @@ describe('RiskInputsTab', () => { const { queryByTestId } = render( - + ); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx index 48f004cbd7069..f4514c6bf8f2b 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx @@ -14,6 +14,8 @@ import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; import { get } from 'lodash/fp'; +import { AlertPreviewButton } from '../../../../../flyout/shared/components/alert_preview_button'; +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; import { useGlobalTime } from '../../../../../common/containers/use_global_time'; import { useQueryInspector } from '../../../../../common/components/page/manage_query'; import { formatRiskScore } from '../../../../common'; @@ -40,6 +42,7 @@ import { ActionColumn } from '../../components/action_column'; export interface RiskInputsTabProps extends Record { entityType: RiskScoreEntity; entityName: string; + scopeId: string; } const FIRST_RECORD_PAGINATION = { @@ -47,9 +50,10 @@ const FIRST_RECORD_PAGINATION = { querySize: 1, }; +export const EXPAND_ALERT_TEST_ID = 'risk-input-alert-preview-button'; export const RISK_INPUTS_TAB_QUERY_ID = 'RiskInputsTabQuery'; -export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) => { +export const RiskInputsTab = ({ entityType, entityName, scopeId }: RiskInputsTabProps) => { const { setQuery, deleteQuery } = useGlobalTime(); const [selectedItems, setSelectedItems] = useState([]); @@ -96,9 +100,26 @@ export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) => }), [] ); + const isPreviewEnabled = useIsExperimentalFeatureEnabled('entityAlertPreviewEnabled'); const inputColumns: Array> = useMemo( () => [ + ...(isPreviewEnabled + ? [ + { + render: (data: InputAlert) => ( + + ), + width: '5%', + }, + ] + : []), + { name: ( render: formatContribution, }, ], - [] + [isPreviewEnabled, scopeId] ); const [isAssetCriticalityEnabled] = useUiSetting$(ENABLE_ASSET_CRITICALITY_SETTING); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx index 8596c7869edcb..0b08c8976cf67 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx @@ -37,7 +37,7 @@ import { CriticalityLevels, CriticalityModifiers, } from '../../../../common/entity_analytics/asset_criticality'; -import { RiskScoreDocLink } from '../risk_score_onboarding/risk_score_doc_link'; +import { EntityAnalyticsLearnMoreLink } from '../risk_score_onboarding/entity_analytics_doc_link'; import { BETA } from '../risk_score_onboarding/translations'; import { AssetCriticalityBadge } from '../asset_criticality'; @@ -262,14 +262,7 @@ export const RiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => - - } - /> + diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/translations.ts index d072607544d4f..4ad2314afb859 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/translations.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/translations.ts @@ -40,6 +40,13 @@ export const RISK_SCORE_TITLE = (riskEntity: RiskScoreEntity) => }, }); +export const RISK_SCORING_TITLE = i18n.translate( + 'xpack.securitySolution.riskScore.overview.riskScoringTitle', + { + defaultMessage: 'Entity Risk Scoring', + } +); + export const ENTITY_RISK_LEVEL = (riskEntity: RiskScoreEntity) => i18n.translate('xpack.securitySolution.entityAnalytics.riskDashboard.riskLevelTitle', { defaultMessage: '{riskEntity} risk level', diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/entity_analytics_doc_link.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/entity_analytics_doc_link.tsx new file mode 100644 index 0000000000000..b0d421c743411 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/entity_analytics_doc_link.tsx @@ -0,0 +1,37 @@ +/* + * 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 { EuiLink } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { useKibana } from '../../../common/lib/kibana'; + +const EntityAnalyticsLearnMoreLinkComponent = ({ title }: { title?: string | React.ReactNode }) => { + const { docLinks } = useKibana().services; + const entityAnalyticsLinks = docLinks.links.securitySolution.entityAnalytics; + + return ( + + {title ? ( + title + ) : ( + + )} + + ); +}; + +export const EntityAnalyticsLearnMoreLink = React.memo(EntityAnalyticsLearnMoreLinkComponent); + +EntityAnalyticsLearnMoreLink.displayName = 'EntityAnalyticsLearnMoreLink'; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_doc_link.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_doc_link.tsx deleted file mode 100644 index ca7b67134b29d..0000000000000 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_doc_link.tsx +++ /dev/null @@ -1,46 +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 { EuiLink } from '@elastic/eui'; -import React, { useMemo } from 'react'; -import { RiskScoreEntity } from '../../../../common/search_strategy'; -import { useKibana } from '../../../common/lib/kibana'; -import { LEARN_MORE } from '../entity_analytics_risk_score/translations'; - -const useLearnMoreLinkForEntity = (riskScoreEntity?: RiskScoreEntity) => { - const { docLinks } = useKibana().services; - const entityAnalyticsLinks = docLinks.links.securitySolution.entityAnalytics; - return useMemo(() => { - if (!riskScoreEntity) { - return entityAnalyticsLinks.entityRiskScoring; - } - if (riskScoreEntity === RiskScoreEntity.user) { - return entityAnalyticsLinks.userRiskScore; - } - return entityAnalyticsLinks.hostRiskScore; - }, [riskScoreEntity, entityAnalyticsLinks]); -}; - -const RiskScoreDocLinkComponent = ({ - riskScoreEntity, - title, -}: { - riskScoreEntity?: RiskScoreEntity; - title?: string | React.ReactNode; -}) => { - const learnMoreLink = useLearnMoreLinkForEntity(riskScoreEntity); - - return ( - - {title ? title : LEARN_MORE(riskScoreEntity)} - - ); -}; - -export const RiskScoreDocLink = React.memo(RiskScoreDocLinkComponent); - -RiskScoreDocLink.displayName = 'RiskScoreDocLink'; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.tsx index f75204ad5ffd2..6878f9aab0512 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.tsx @@ -36,7 +36,7 @@ const RiskScoreEnableButtonComponent = ({ }) => { const spaceId = useSpaceId(); const { http, dashboard, ...startServices } = useKibana().services; - const { renderDocLink, renderDashboardLink } = useRiskScoreToastContent(riskScoreEntity); + const { renderDocLink, renderDashboardLink } = useRiskScoreToastContent(); const { fetch, isLoading } = useFetch(REQUEST_NAMES.ENABLE_RISK_SCORE, installRiskScoreModule); const isRiskEngineEnabled = useIsExperimentalFeatureEnabled('riskScoringRoutesEnabled'); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.tsx index 70f28df4420f9..87ef0df078c09 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.tsx @@ -30,7 +30,7 @@ const RiskScoreRestartButtonComponent = ({ ); const spaceId = useSpaceId(); - const { renderDocLink } = useRiskScoreToastContent(riskScoreEntity); + const { renderDocLink } = useRiskScoreToastContent(); const { http, ...startServices } = useKibana().services; const onClick = useCallback(async () => { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/use_risk_score_toast_content.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/use_risk_score_toast_content.tsx index 7df01d9f8bd86..30fdc0f9204f2 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/use_risk_score_toast_content.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/use_risk_score_toast_content.tsx @@ -10,21 +10,20 @@ import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { RiskScoreEntity } from '../../../../common/search_strategy'; -import { RiskScoreDocLink } from './risk_score_doc_link'; +import { EntityAnalyticsLearnMoreLink } from './entity_analytics_doc_link'; const StyledButton = styled(EuiButton)` float: right; `; -export const useRiskScoreToastContent = (riskScoreEntity: RiskScoreEntity) => { +export const useRiskScoreToastContent = () => { const renderDocLink = useCallback( (message: string) => ( <> - {message} + {message} ), - [riskScoreEntity] + [] ); const renderDashboardLink = useCallback( (message: string, targetUrl: string) => ( diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.stories.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.stories.tsx index 2d0d8f9525713..76105e78a815b 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.stories.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.stories.tsx @@ -10,11 +10,11 @@ import type { Story } from '@storybook/react'; import { TestProvider } from '@kbn/expandable-flyout/src/test/provider'; import { StorybookProviders } from '../../../common/mock/storybook_providers'; import { mockRiskScoreState } from '../../../timelines/components/side_panel/new_user_detail/__mocks__'; -import { RiskSummary } from './risk_summary'; +import { FlyoutRiskSummary } from './risk_summary'; export default { - component: RiskSummary, - title: 'Components/RiskSummary', + component: FlyoutRiskSummary, + title: 'Components/FlyoutRiskSummary', }; export const Default: Story = () => { @@ -22,7 +22,7 @@ export const Default: Story = () => {

    - {}} riskScoreData={{ ...mockRiskScoreState, data: [] }} queryId={'testQuery'} @@ -39,7 +39,7 @@ export const PreviewMode: Story = () => {
    - { }; }); -describe('RiskSummary', () => { +describe('FlyoutRiskSummary', () => { beforeEach(() => { mockVisualizationEmbeddable.mockClear(); }); @@ -45,7 +45,7 @@ describe('RiskSummary', () => { it('renders risk summary table with alerts only', () => { const { getByTestId, queryByTestId } = render( - {}} @@ -76,7 +76,7 @@ describe('RiskSummary', () => { const { getByTestId } = render( - {}} @@ -108,7 +108,7 @@ describe('RiskSummary', () => { it('renders risk summary table when riskScoreData is empty', () => { const { getByTestId } = render( - {}} @@ -122,7 +122,7 @@ describe('RiskSummary', () => { it('risk summary header does not render link when riskScoreData is loading', () => { const { queryByTestId } = render( - {}} @@ -137,7 +137,7 @@ describe('RiskSummary', () => { it('risk summary header does not render expand icon when in preview mode', () => { const { queryByTestId } = render( - {}} @@ -154,7 +154,7 @@ describe('RiskSummary', () => { it('renders visualization embeddable', () => { const { getByTestId } = render( - {}} @@ -169,7 +169,7 @@ describe('RiskSummary', () => { it('renders updated at', () => { const { getByTestId } = render( - {}} @@ -184,7 +184,7 @@ describe('RiskSummary', () => { it('builds lens attributes for host risk score', () => { render( - {}} @@ -211,7 +211,7 @@ describe('RiskSummary', () => { it('builds lens cases attachment metadata for host risk score', () => { render( - {}} @@ -233,7 +233,7 @@ describe('RiskSummary', () => { it('builds lens cases attachment metadata for user risk score', () => { render( - {}} @@ -255,7 +255,7 @@ describe('RiskSummary', () => { it('builds lens attributes for user risk score', () => { render( - {}} diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx index ce70b5fb211e7..8d0b00129057e 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx @@ -55,7 +55,7 @@ export interface RiskSummaryProps { isPreviewMode?: boolean; } -const RiskSummaryComponent = ({ +const FlyoutRiskSummaryComponent = ({ riskScoreData, recalculatingScore, queryId, @@ -287,5 +287,5 @@ const RiskSummaryComponent = ({ ); }; -export const RiskSummary = React.memo(RiskSummaryComponent); -RiskSummary.displayName = 'RiskSummary'; +export const FlyoutRiskSummary = React.memo(FlyoutRiskSummaryComponent); +FlyoutRiskSummary.displayName = 'RiskSummary'; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_panel.test.tsx similarity index 91% rename from x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_panel.test.tsx index 75722d781bbc7..cfe1e4ec898c4 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_panel.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { render } from '@testing-library/react'; import { TestProviders } from '../../common/mock'; -import type { RiskEntity } from './risk_summary'; +import type { RiskEntity } from './risk_summary_panel'; import * as i18n from '../../common/components/event_details/cti_details/translations'; -import { RiskSummary } from './risk_summary'; +import { RiskSummaryPanel } from './risk_summary_panel'; import { RiskScoreEntity, RiskSeverity } from '../../../common/search_strategy'; import { getEmptyValue } from '../../common/components/empty_value'; @@ -46,7 +46,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( const { getByText } = render( - + ); @@ -67,7 +67,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( } as RiskEntity; const { getByTestId } = render( - + ); @@ -86,7 +86,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( } as RiskEntity; const { getByText } = render( - + ); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_panel.tsx similarity index 89% rename from x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.tsx rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_panel.tsx index df3933ae50e5b..80d0558f5cf55 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_panel.tsx @@ -17,7 +17,7 @@ import { RiskScoreLevel } from './severity/common'; import type { RiskSeverity } from '../../../common/search_strategy'; import { RiskScoreEntity } from '../../../common/search_strategy'; import { getEmptyValue } from '../../common/components/empty_value'; -import { RiskScoreDocLink } from './risk_score_onboarding/risk_score_doc_link'; +import { EntityAnalyticsLearnMoreLink } from './risk_score_onboarding/entity_analytics_doc_link'; import { RiskScoreHeaderTitle } from './risk_score_onboarding/risk_score_header_title'; import type { HostRisk, UserRisk } from '../api/types'; @@ -35,7 +35,7 @@ interface UserRiskEntity { export type RiskEntity = HostRiskEntity | UserRiskEntity; -const RiskSummaryComponent: React.FC = ({ risk, riskEntity, originalRisk }) => { +const RiskSummaryPanelComponent: React.FC = ({ risk, riskEntity, originalRisk }) => { const currentRiskScore = riskEntity === RiskScoreEntity.host ? risk?.result?.[0]?.host?.risk?.calculated_level @@ -64,10 +64,7 @@ const RiskSummaryComponent: React.FC = ({ risk, riskEntity, original values={{ riskEntity, riskScoreDocumentationLink: ( - + ), }} /> @@ -103,4 +100,4 @@ const RiskSummaryComponent: React.FC = ({ risk, riskEntity, original ); }; -export const RiskSummary = React.memo(RiskSummaryComponent); +export const RiskSummaryPanel = React.memo(RiskSummaryPanelComponent); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx index 1fce080352a08..fec6a1efaa08f 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.test.tsx @@ -16,7 +16,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_ex import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context'; import { mockContextValue } from '../../shared/mocks/mock_context'; import { DocumentDetailsPreviewPanelKey } from '../../shared/constants/panel_keys'; -import { ALERT_PREVIEW_BANNER } from '../../preview'; +import { ALERT_PREVIEW_BANNER } from '../../preview/constants'; import { DocumentDetailsContext } from '../../shared/context'; jest.mock('../hooks/use_paginated_alerts'); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx index bf1a28201fc87..5253aa1cd272b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/correlations_details_alerts_table.tsx @@ -7,16 +7,14 @@ import type { ReactElement, ReactNode } from 'react'; import React, { type FC, useMemo, useCallback } from 'react'; -import { type Criteria, EuiBasicTable, formatDate, EuiButtonIcon } from '@elastic/eui'; +import { type Criteria, EuiBasicTable, formatDate } from '@elastic/eui'; import { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import type { Filter } from '@kbn/es-query'; import { isRight } from 'fp-ts/lib/Either'; -import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { ALERT_REASON, ALERT_RULE_NAME } from '@kbn/rule-data-utils'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; -import { useDocumentDetailsContext } from '../../shared/context'; import { CORRELATIONS_DETAILS_ALERT_PREVIEW_BUTTON_TEST_ID } from './test_ids'; import { CellTooltipWrapper } from '../../shared/components/cell_tooltip_wrapper'; import type { DataProvider } from '../../../../../common/types'; @@ -26,51 +24,11 @@ import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { InvestigateInTimelineButton } from '../../../../common/components/event_details/table/investigate_in_timeline_button'; import { ACTION_INVESTIGATE_IN_TIMELINE } from '../../../../detections/components/alerts_table/translations'; import { getDataProvider } from '../../../../common/components/event_details/table/use_action_cell_data_provider'; -import { DocumentDetailsPreviewPanelKey } from '../../shared/constants/panel_keys'; -import { ALERT_PREVIEW_BANNER } from '../../preview'; +import { AlertPreviewButton } from '../../../shared/components/alert_preview_button'; export const TIMESTAMP_DATE_FORMAT = 'MMM D, YYYY @ HH:mm:ss.SSS'; const dataProviderLimit = 5; -interface AlertPreviewButtonProps { - /** - * Id of the document - */ - id: string; - /** - * Name of the index used in the parent's page - */ - indexName: string; -} - -const AlertPreviewButton: FC = ({ id, indexName }) => { - const { openPreviewPanel } = useExpandableFlyoutApi(); - const { scopeId } = useDocumentDetailsContext(); - - const openAlertPreview = useCallback( - () => - openPreviewPanel({ - id: DocumentDetailsPreviewPanelKey, - params: { - id, - indexName, - scopeId, - isPreviewMode: true, - banner: ALERT_PREVIEW_BANNER, - }, - }), - [openPreviewPanel, id, indexName, scopeId] - ); - - return ( - - ); -}; - export interface CorrelationsDetailsAlertsTableProps { /** * Text to display in the ExpandablePanel title section @@ -172,7 +130,12 @@ export const CorrelationsDetailsAlertsTable: FC) => ( - + ), width: '5%', }, @@ -247,7 +210,7 @@ export const CorrelationsDetailsAlertsTable: FC> = memo(({ path }) => { const { telemetry } = useKibana().services; const { openLeftPanel } = useExpandableFlyoutApi(); - const { eventId, indexName, scopeId, getFieldsData } = useDocumentDetailsContext(); + const { eventId, indexName, scopeId, getFieldsData, isPreview } = useDocumentDetailsContext(); const eventKind = getField(getFieldsData('event.kind')); const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled( 'securitySolutionNotesEnabled' @@ -43,11 +43,11 @@ export const LeftPanel: FC> = memo(({ path }) => { eventKind === EventKind.signal ? [tabs.insightsTab, tabs.investigationTab, tabs.responseTab] : [tabs.insightsTab]; - if (securitySolutionNotesEnabled) { + if (securitySolutionNotesEnabled && !isPreview) { tabList.push(tabs.notesTab); } return tabList; - }, [eventKind, securitySolutionNotesEnabled]); + }, [eventKind, isPreview, securitySolutionNotesEnabled]); const selectedTabId = useMemo(() => { const defaultTab = tabsDisplayed[0].id; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/preview/constants.ts b/x-pack/plugins/security_solution/public/flyout/document_details/preview/constants.ts new file mode 100644 index 0000000000000..0c07633c4c11e --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/preview/constants.ts @@ -0,0 +1,19 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ALERT_PREVIEW_BANNER = { + title: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.correlations.alertPreviewTitle', + { + defaultMessage: 'Preview alert details', + } + ), + backgroundColor: 'warning', + textColor: 'warning', +}; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/preview/index.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/preview/index.tsx index ff65d4c264c42..6b5eb418774a1 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/preview/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/preview/index.tsx @@ -8,7 +8,6 @@ import type { FC } from 'react'; import React, { memo } from 'react'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import { i18n } from '@kbn/i18n'; import { DocumentDetailsPreviewPanelKey } from '../shared/constants/panel_keys'; import { useTabs } from '../right/hooks/use_tabs'; import { useFlyoutIsExpandable } from '../right/hooks/use_flyout_is_expandable'; @@ -18,17 +17,7 @@ import { PanelHeader } from '../right/header'; import { PanelContent } from '../right/content'; import { PreviewPanelFooter } from './footer'; import type { RightPanelTabType } from '../right/tabs'; - -export const ALERT_PREVIEW_BANNER = { - title: i18n.translate( - 'xpack.securitySolution.flyout.left.insights.correlations.alertPreviewTitle', - { - defaultMessage: 'Preview alert details', - } - ), - backgroundColor: 'warning', - textColor: 'warning', -}; +import { ALERT_PREVIEW_BANNER } from './constants'; /** * Panel to be displayed in the document details expandable flyout on top of right section diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx index e48b5f8fd4f9d..ddcb5a805cd46 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx @@ -67,7 +67,7 @@ export interface HostEntityOverviewProps { export const HOST_PREVIEW_BANNER = { title: i18n.translate('xpack.securitySolution.flyout.right.host.hostPreviewTitle', { - defaultMessage: 'Preview host', + defaultMessage: 'Preview host details', }), backgroundColor: 'warning', textColor: 'warning', @@ -187,7 +187,7 @@ export const HostEntityOverview: React.FC = ({ hostName {HOST_RISK_LEVEL} - + ), diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx index 47938e5212e7c..f8b9a49abc306 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx @@ -68,7 +68,7 @@ export interface UserEntityOverviewProps { export const USER_PREVIEW_BANNER = { title: i18n.translate('xpack.securitySolution.flyout.right.user.userPreviewTitle', { - defaultMessage: 'Preview user', + defaultMessage: 'Preview user details', }), backgroundColor: 'warning', textColor: 'warning', @@ -187,7 +187,7 @@ export const UserEntityOverview: React.FC = ({ userName {USER_RISK_LEVEL} - + ), diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx index fdda420616cc6..afe0bebcf802b 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx @@ -41,16 +41,22 @@ jest.mock('../../../entity_analytics/api/hooks/use_risk_score', () => ({ describe('HostDetailsPanel', () => { it('render risk inputs panel', () => { - const { getByTestId } = render(, { - wrapper: TestProviders, - }); + const { getByTestId } = render( + , + { + wrapper: TestProviders, + } + ); expect(getByTestId(RISK_INPUTS_TAB_TEST_ID)).toBeInTheDocument(); }); it("doesn't render risk inputs panel when no alerts ids are provided", () => { - const { queryByTestId } = render(, { - wrapper: TestProviders, - }); + const { queryByTestId } = render( + , + { + wrapper: TestProviders, + } + ); expect(queryByTestId(RISK_INPUTS_TAB_TEST_ID)).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx index 853a68c4fb95e..ba34ac3d8aa3a 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx @@ -18,6 +18,7 @@ import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine export interface HostDetailsPanelProps extends Record { isRiskScoreExist: boolean; name: string; + scopeId: string; } export interface HostDetailsExpandableFlyoutProps extends FlyoutPanelProps { key: 'host_details'; @@ -25,18 +26,18 @@ export interface HostDetailsExpandableFlyoutProps extends FlyoutPanelProps { } export const HostDetailsPanelKey: HostDetailsExpandableFlyoutProps['key'] = 'host_details'; -export const HostDetailsPanel = ({ name, isRiskScoreExist }: HostDetailsPanelProps) => { +export const HostDetailsPanel = ({ name, isRiskScoreExist, scopeId }: HostDetailsPanelProps) => { // Temporary implementation while Host details left panel don't have Asset tabs const [tabs, selectedTabId, setSelectedTabId] = useMemo(() => { const isRiskScoreTabAvailable = isRiskScoreExist && name; return [ isRiskScoreTabAvailable - ? [getRiskInputTab({ entityName: name, entityType: RiskScoreEntity.host })] + ? [getRiskInputTab({ entityName: name, entityType: RiskScoreEntity.host, scopeId })] : [], EntityDetailsLeftPanelTab.RISK_INPUTS, () => {}, ]; - }, [name, isRiskScoreExist]); + }, [name, isRiskScoreExist, scopeId]); return ( <> diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx index 4632c20e517af..e550da28b532a 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_preview/footer.tsx @@ -47,7 +47,7 @@ export const HostPreviewPanelFooter = ({ {i18n.translate('xpack.securitySolution.flyout.host.preview.viewDetailsLabel', { - defaultMessage: 'Open host details flyout', + defaultMessage: 'Show full host details', })} diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx index 26dcb462a1609..af3eae38fc1f8 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/content.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiHorizontalRule } from '@elastic/eui'; import { AssetCriticalityAccordion } from '../../../entity_analytics/components/asset_criticality/asset_criticality_selector'; -import { RiskSummary } from '../../../entity_analytics/components/risk_summary_flyout/risk_summary'; +import { FlyoutRiskSummary } from '../../../entity_analytics/components/risk_summary_flyout/risk_summary'; import type { RiskScoreState } from '../../../entity_analytics/api/hooks/use_risk_score'; import type { RiskScoreEntity, HostItem } from '../../../../common/search_strategy'; import { FlyoutBody } from '../../shared/components/flyout_body'; @@ -49,7 +49,7 @@ export const HostPanelContent = ({ {riskScoreState.isModuleEnabled && riskScoreState.data?.length !== 0 && ( <> - openTabPanel(), [openTabPanel]); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.test.tsx index 2c88a0ecdfb0b..bdff465e0b982 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.test.tsx @@ -20,6 +20,7 @@ describe('LeftPanel', () => { }} isRiskScoreExist user={{ name: 'test user', email: [] }} + scopeId={'scopeId'} />, { wrapper: TestProviders, @@ -39,6 +40,7 @@ describe('LeftPanel', () => { }} isRiskScoreExist={false} user={{ name: 'test user', email: [] }} + scopeId={'scopeId'} />, { wrapper: TestProviders, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx index 3a682ba125864..757c6799a6da1 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx @@ -27,6 +27,7 @@ export interface UserDetailsPanelProps extends Record { isRiskScoreExist: boolean; user: UserParam; path?: PanelPath; + scopeId: string; } export interface UserDetailsExpandableFlyoutProps extends FlyoutPanelProps { key: 'user_details'; @@ -34,9 +35,14 @@ export interface UserDetailsExpandableFlyoutProps extends FlyoutPanelProps { } export const UserDetailsPanelKey: UserDetailsExpandableFlyoutProps['key'] = 'user_details'; -export const UserDetailsPanel = ({ isRiskScoreExist, user, path }: UserDetailsPanelProps) => { +export const UserDetailsPanel = ({ + isRiskScoreExist, + user, + path, + scopeId, +}: UserDetailsPanelProps) => { const managedUser = useManagedUser(user.name, user.email); - const tabs = useTabs(managedUser.data, user.name, isRiskScoreExist); + const tabs = useTabs(managedUser.data, user.name, isRiskScoreExist, scopeId); const { selectedTabId, setSelectedTabId } = useSelectedTab(isRiskScoreExist, user, tabs, path); if (managedUser.isLoading) return ; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx index 358dd5357ae2f..3a6814a28e62c 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx @@ -25,7 +25,8 @@ import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_ export const useTabs = ( managedUser: ManagedUserHits, name: string, - isRiskScoreExist: boolean + isRiskScoreExist: boolean, + scopeId: string ): LeftPanelTabsType => useMemo(() => { const tabs: LeftPanelTabsType = []; @@ -37,6 +38,7 @@ export const useTabs = ( getRiskInputTab({ entityName: name, entityType: RiskScoreEntity.user, + scopeId, }) ); } @@ -50,7 +52,7 @@ export const useTabs = ( } return tabs; - }, [isRiskScoreExist, managedUser, name]); + }, [isRiskScoreExist, managedUser, name, scopeId]); const getOktaTab = (oktaManagedUser: ManagedUserHit) => ({ id: EntityDetailsLeftPanelTab.OKTA, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx index 5fb1b73e1683b..7f55feb7a347c 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_preview/footer.tsx @@ -47,7 +47,7 @@ export const UserPreviewPanelFooter = ({ {i18n.translate('xpack.securitySolution.flyout.user.preview.viewDetailsLabel', { - defaultMessage: 'Open user details flyout', + defaultMessage: 'Show full user details', })} diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx index d7b01fe8905d5..1631f5c1bc58b 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx @@ -12,7 +12,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_exper import { AssetCriticalityAccordion } from '../../../entity_analytics/components/asset_criticality/asset_criticality_selector'; import { OBSERVED_USER_QUERY_ID } from '../../../explore/users/containers/users/observed_details'; -import { RiskSummary } from '../../../entity_analytics/components/risk_summary_flyout/risk_summary'; +import { FlyoutRiskSummary } from '../../../entity_analytics/components/risk_summary_flyout/risk_summary'; import type { RiskScoreState } from '../../../entity_analytics/api/hooks/use_risk_score'; import { ManagedUser } from '../../../timelines/components/side_panel/new_user_detail/managed_user'; import type { ManagedUserData } from '../../../timelines/components/side_panel/new_user_detail/types'; @@ -58,7 +58,7 @@ export const UserPanelContent = ({ {riskScoreState.isModuleEnabled && riskScoreState.data?.length !== 0 && ( <> - openPanelTab(), [openPanelTab]); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/components/alert_preview_button.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/alert_preview_button.test.tsx new file mode 100644 index 0000000000000..f478812f96f93 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/alert_preview_button.test.tsx @@ -0,0 +1,65 @@ +/* + * 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 { fireEvent, render } from '@testing-library/react'; +import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout'; +import React from 'react'; +import { AlertPreviewButton } from './alert_preview_button'; +import { DocumentDetailsPreviewPanelKey } from '../../document_details/shared/constants/panel_keys'; +import { ALERT_PREVIEW_BANNER } from '../../document_details/preview/constants'; + +const mockOpenPreviewPanel = jest.fn(); +jest.mock('@kbn/expandable-flyout', () => { + return { + useExpandableFlyoutApi: () => ({ + openPreviewPanel: mockOpenPreviewPanel, + }), + }; +}); + +describe('AlertPreviewButton', () => { + it('renders the icon', () => { + const { getByTestId } = render( + , + { wrapper: ExpandableFlyoutProvider } + ); + expect(getByTestId('alertPreviewButton')).toBeInTheDocument(); + }); + + it('opens the preview panel when clicked', () => { + const id = '1'; + const indexName = 'index'; + const scopeId = 'scope'; + + const { getByTestId } = render( + , + { wrapper: ExpandableFlyoutProvider } + ); + fireEvent.click(getByTestId('alertPreviewButton')); + + expect(mockOpenPreviewPanel).toHaveBeenCalledWith({ + id: DocumentDetailsPreviewPanelKey, + params: { + id, + indexName, + scopeId, + isPreviewMode: true, + banner: ALERT_PREVIEW_BANNER, + }, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/components/alert_preview_button.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/alert_preview_button.tsx new file mode 100644 index 0000000000000..62426476e3c1f --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/alert_preview_button.tsx @@ -0,0 +1,72 @@ +/* + * 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 { EuiButtonIcon } from '@elastic/eui'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import React, { useCallback } from 'react'; +import type { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { ALERT_PREVIEW_BANNER } from '../../document_details/preview/constants'; +import { DocumentDetailsPreviewPanelKey } from '../../document_details/shared/constants/panel_keys'; + +interface AlertPreviewButtonProps { + /** + * Name of the index used in the parent's page + */ + indexName: string; + /** + * Id of the alert to preview + */ + id: string; + /** + * Data attribute used for testing. + */ + 'data-test-subj'?: string; + /** + * Maintain backwards compatibility // TODO remove when possible + */ + scopeId: string; +} + +/** + * Icon button showed on tables to launch a preview of the alert details panel. + */ +export const AlertPreviewButton: FC = ({ + id, + indexName, + 'data-test-subj': dataTestSubj, + scopeId, +}) => { + const { openPreviewPanel } = useExpandableFlyoutApi(); + + const openAlertPreview = useCallback( + () => + openPreviewPanel({ + id: DocumentDetailsPreviewPanelKey, + params: { + id, + indexName, + scopeId, + isPreviewMode: true, + banner: ALERT_PREVIEW_BANNER, + }, + }), + [openPreviewPanel, id, indexName, scopeId] + ); + + return ( + + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx index 657b6847e7839..a6b2951615381 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx @@ -6,18 +6,18 @@ */ import { memo, useMemo } from 'react'; -import type { KillOrSuspendProcessRequestBody } from '../../../../../common/endpoint/types'; import { parsedPidOrEntityIdParameter } from '../lib/utils'; import { useSendKillProcessRequest } from '../../../hooks/response_actions/use_send_kill_process_endpoint_request'; import type { ActionRequestComponentProps } from '../types'; import { useConsoleActionSubmitter } from '../hooks/use_console_action_submitter'; +import type { KillProcessRequestBody } from '../../../../../common/endpoint/types'; export const KillProcessActionResult = memo< ActionRequestComponentProps<{ pid?: string[]; entityId?: string[] }> >(({ command, setStore, store, status, setStatus, ResultComponent }) => { const actionCreator = useSendKillProcessRequest(); - const actionRequestBody = useMemo(() => { + const actionRequestBody = useMemo(() => { const endpointId = command.commandDefinition?.meta?.endpointId; const parameters = parsedPidOrEntityIdParameter(command.args.args); @@ -30,7 +30,7 @@ export const KillProcessActionResult = memo< : undefined; }, [command.args.args, command.commandDefinition?.meta?.endpointId]); - return useConsoleActionSubmitter({ + return useConsoleActionSubmitter({ ResultComponent, setStore, store, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx index fbb18aabd7b00..70893689c0439 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx @@ -8,8 +8,8 @@ import { memo, useMemo } from 'react'; import { parsedPidOrEntityIdParameter } from '../lib/utils'; import type { - KillOrSuspendProcessRequestBody, SuspendProcessActionOutputContent, + SuspendProcessRequestBody, } from '../../../../../common/endpoint/types'; import { useSendSuspendProcessRequest } from '../../../hooks/response_actions/use_send_suspend_process_endpoint_request'; import type { ActionRequestComponentProps } from '../types'; @@ -20,7 +20,7 @@ export const SuspendProcessActionResult = memo< >(({ command, setStore, store, status, setStatus, ResultComponent }) => { const actionCreator = useSendSuspendProcessRequest(); - const actionRequestBody = useMemo(() => { + const actionRequestBody = useMemo(() => { const endpointId = command.commandDefinition?.meta?.endpointId; const parameters = parsedPidOrEntityIdParameter(command.args.args); @@ -33,10 +33,7 @@ export const SuspendProcessActionResult = memo< : undefined; }, [command.args.args, command.commandDefinition?.meta?.endpointId]); - return useConsoleActionSubmitter< - KillOrSuspendProcessRequestBody, - SuspendProcessActionOutputContent - >({ + return useConsoleActionSubmitter({ ResultComponent, setStore, store, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts index 84624c2ce595b..b57bd57ecd85b 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts @@ -571,9 +571,13 @@ const adjustCommandsForSentinelOne = ({ }: { commandList: CommandDefinition[]; }): CommandDefinition[] => { + const featureFlags = ExperimentalFeaturesService.get(); + const isKillProcessEnabled = featureFlags.responseActionsSentinelOneKillProcessEnabled; + return commandList.map((command) => { if ( command.name === 'status' || + (command.name === 'kill-process' && !isKillProcessEnabled) || !isAgentTypeAndActionSupported( 'sentinel_one', RESPONSE_CONSOLE_COMMAND_TO_API_COMMAND_MAP[command.name as ConsoleResponseActionCommands], diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/utils.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/utils.ts index d5c40347ebf80..41c57feb5c76c 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/utils.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/utils.ts @@ -4,12 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { ResponseActionParametersWithPidOrEntityId } from '../../../../../common/endpoint/types'; +import type { + ResponseActionParametersWithEntityId, + ResponseActionParametersWithPid, +} from '../../../../../common/endpoint/types'; export const parsedPidOrEntityIdParameter = (parameters: { pid?: string[]; entityId?: string[]; -}): ResponseActionParametersWithPidOrEntityId => { +}): ResponseActionParametersWithPid | ResponseActionParametersWithEntityId => { if (parameters.pid) { return { pid: Number(parameters.pid[0]) }; } diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx index bf13fec9086b1..48cc21f4f5a55 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx @@ -39,6 +39,7 @@ import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint import { useGetEndpointActionList as _useGetEndpointActionList } from '../../../hooks/response_actions/use_get_endpoint_action_list'; import { OUTPUT_MESSAGES } from '../translations'; import { EndpointActionGenerator } from '../../../../../common/endpoint/data_generators/endpoint_action_generator'; +import type { ExperimentalFeatures } from '../../../../../common'; const useGetEndpointActionListMock = _useGetEndpointActionList as jest.Mock; @@ -1490,6 +1491,17 @@ describe('Response actions history', () => { }); describe('Actions filter', () => { + let featureFlags: Partial; + + beforeEach(() => { + featureFlags = { + responseActionUploadEnabled: true, + responseActionScanEnabled: false, + }; + + mockedContext.setExperimentalFlag(featureFlags); + }); + const filterPrefix = 'actions-filter'; it('should have a search bar', () => { @@ -1505,7 +1517,10 @@ describe('Response actions history', () => { }); it('should show a list of actions (without `scan`) when opened', () => { - mockedContext.setExperimentalFlag({ responseActionUploadEnabled: true }); + mockedContext.setExperimentalFlag({ + ...featureFlags, + responseActionScanEnabled: false, + }); render(); const { getByTestId, getAllByTestId } = renderResult; @@ -1529,7 +1544,7 @@ describe('Response actions history', () => { it('should show a list of actions (with `scan`) when opened', () => { mockedContext.setExperimentalFlag({ - responseActionUploadEnabled: true, + ...featureFlags, responseActionScanEnabled: true, }); render(); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts index 9da25023f0778..02c367976c163 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/automated_response_actions/automated_response_actions.cy.ts @@ -20,7 +20,8 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; -describe( +// Failing: See https://github.com/elastic/kibana/issues/168340 +describe.skip( 'Automated Response Actions', { tags: ['@ess', '@serverless'], diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts index 12cdfcfa6e09c..b196030cae34a 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts @@ -28,7 +28,8 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; -describe('Endpoints page', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/168284 +describe.skip('Endpoints page', { tags: ['@ess', '@serverless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts index 042031b301185..b8b88cbd95c34 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts @@ -21,7 +21,8 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe('Response console', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/170373 +describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts index f89f2a6f62ecf..c90ae66c7166a 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts @@ -26,7 +26,8 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe('Response console', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/173464 +describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts index d11b7210713a8..4ad892155cb54 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/release.cy.ts @@ -27,7 +27,8 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe('Response console', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/172326 +describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/scan.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/scan.cy.ts index ba105aa8cbc0a..e4faa0764a6cb 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/scan.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/scan.cy.ts @@ -20,7 +20,8 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// Failing: See https://github.com/elastic/kibana/issues/187932 +describe.skip( 'Response console', { env: { diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts index 2d86f15f81d40..b17d34ab4e463 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts @@ -9,8 +9,8 @@ import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-quer import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { - KillOrSuspendProcessRequestBody, ResponseActionApiResponse, + KillProcessRequestBody, } from '../../../../common/endpoint/types'; import { killProcess } from '../../../common/lib/process_actions'; @@ -22,15 +22,11 @@ export const useSendKillProcessRequest = ( customOptions?: UseMutationOptions< ResponseActionApiResponse, IHttpFetchError, - KillOrSuspendProcessRequestBody + KillProcessRequestBody > -): UseMutationResult< - ResponseActionApiResponse, - IHttpFetchError, - KillOrSuspendProcessRequestBody -> => { - return useMutation( - (processData: KillOrSuspendProcessRequestBody) => { +): UseMutationResult => { + return useMutation( + (processData: KillProcessRequestBody) => { return killProcess(processData); }, customOptions diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts index d6c2f56cb627f..787344ffd54dc 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts @@ -9,8 +9,8 @@ import { useMutation } from '@tanstack/react-query'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { - KillOrSuspendProcessRequestBody, ResponseActionApiResponse, + SuspendProcessRequestBody, } from '../../../../common/endpoint/types'; import { suspendProcess } from '../../../common/lib/process_actions'; @@ -22,15 +22,11 @@ export const useSendSuspendProcessRequest = ( customOptions?: UseMutationOptions< ResponseActionApiResponse, IHttpFetchError, - KillOrSuspendProcessRequestBody + SuspendProcessRequestBody > -): UseMutationResult< - ResponseActionApiResponse, - IHttpFetchError, - KillOrSuspendProcessRequestBody -> => { - return useMutation( - (processData: KillOrSuspendProcessRequestBody) => { +): UseMutationResult => { + return useMutation( + (processData: SuspendProcessRequestBody) => { return suspendProcess(processData); }, customOptions diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx index c355cc8bdea0b..7dc11f828187a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx @@ -546,90 +546,294 @@ describe('Event filter form', () => { }); describe('Warnings', () => { - beforeEach(() => { - render(); - }); + describe('duplicate fields', () => { + it('should not show warning text when unique fields are added', async () => { + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value', + }, + { + field: 'file.name', + operator: 'excluded', + type: 'match', + value: 'some other value', + }, + ]; + render(); + expect(await renderResult.findByDisplayValue('some value')).toBeInTheDocument(); - it('should not show warning text when unique fields are added', async () => { - formProps.item.entries = [ - { - field: 'event.category', - operator: 'included', - type: 'match', - value: 'some value', - }, - { - field: 'file.name', - operator: 'excluded', - type: 'match', - value: 'some other value', - }, - ]; - rerender(); - expect(renderResult.queryByTestId('duplicate-fields-warning-message')).toBeNull(); - }); + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + }); - it('should not show warning text when field values are not added', async () => { - formProps.item.entries = [ - { - field: 'event.category', - operator: 'included', - type: 'match', - value: '', - }, - { - field: 'event.category', - operator: 'excluded', - type: 'match', - value: '', - }, - ]; - rerender(); - expect(renderResult.queryByTestId('duplicate-fields-warning-message')).toBeNull(); - }); + it('should not show warning text when field values are not added', async () => { + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: '', + }, + { + field: 'event.category', + operator: 'excluded', + type: 'match', + value: '', + }, + ]; + render(); + expect((await renderResult.findAllByTestId('fieldAutocompleteComboBox')).length).toBe(2); - it('should show warning text when duplicate fields are added with values', async () => { - formProps.item.entries = [ - { - field: 'event.category', - operator: 'included', - type: 'match', - value: 'some value', - }, - { - field: 'event.category', - operator: 'excluded', - type: 'match', - value: 'some other value', - }, - ]; - rerender(); - expect(renderResult.findByTestId('duplicate-fields-warning-message')).not.toBeNull(); + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + }); + + it('should show warning text when duplicate fields are added with values', async () => { + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value', + }, + { + field: 'event.category', + operator: 'excluded', + type: 'match', + value: 'some other value', + }, + ]; + render(); + + expect( + await renderResult.findByTestId('duplicate-fields-warning-message') + ).toBeInTheDocument(); + }); + + describe('in relation with Process Descendant filtering', () => { + it('should not show warning text when event.category is added but feature flag is disabled', async () => { + mockedContext.setExperimentalFlag({ + filterProcessDescendantsForEventFiltersEnabled: false, + }); + + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value 1', + }, + ]; + formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG]; + + render(); + expect(await renderResult.findByDisplayValue('some value 1')).toBeInTheDocument(); + + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + }); + + it('should not show warning text when event.category is added but process descendant filter is disabled', async () => { + mockedContext.setExperimentalFlag({ + filterProcessDescendantsForEventFiltersEnabled: true, + }); + + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value 2', + }, + ]; + formProps.item.tags = []; + + render(); + expect(await renderResult.findByDisplayValue('some value 2')).toBeInTheDocument(); + + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + }); + + it('should not show warning text when event.category is NOT added and process descendant filter is enabled', async () => { + mockedContext.setExperimentalFlag({ + filterProcessDescendantsForEventFiltersEnabled: true, + }); + + formProps.item.entries = [ + { + field: 'event.action', + operator: 'included', + type: 'match', + value: 'some value 3', + }, + ]; + formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG]; + + render(); + expect(await renderResult.findByDisplayValue('some value 3')).toBeInTheDocument(); + + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + }); + + it('should show warning text when event.category is added and process descendant filter is enabled', async () => { + mockedContext.setExperimentalFlag({ + filterProcessDescendantsForEventFiltersEnabled: true, + }); + + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value 4', + }, + ]; + formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG]; + + render(); + expect(await renderResult.findByDisplayValue('some value 4')).toBeInTheDocument(); + + expect( + await renderResult.findByTestId('duplicate-fields-warning-message') + ).toBeInTheDocument(); + }); + + it('should add warning text when switching to process descendant filtering', async () => { + mockedContext.setExperimentalFlag({ + filterProcessDescendantsForEventFiltersEnabled: true, + }); + + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value 5', + }, + ]; + formProps.item.tags = []; + + render(); + expect(await renderResult.findByDisplayValue('some value 5')).toBeInTheDocument(); + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + + // switch to Process Descendant filtering + userEvent.click(renderResult.getByTestId(`${formPrefix}-filterProcessDescendantsButton`)); + rerenderWithLatestProps(); + + expect( + await renderResult.findByTestId('duplicate-fields-warning-message') + ).toBeInTheDocument(); + }); + + it('should remove warning text when switching from process descendant filtering', async () => { + mockedContext.setExperimentalFlag({ + filterProcessDescendantsForEventFiltersEnabled: true, + }); + + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value 6', + }, + ]; + formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG]; + + render(); + + expect( + await renderResult.findByTestId('duplicate-fields-warning-message') + ).toBeInTheDocument(); + + // switch to classic Event filtering + userEvent.click(renderResult.getByTestId(`${formPrefix}-filterEventsButton`)); + rerenderWithLatestProps(); + + expect(await renderResult.findByDisplayValue('some value 6')).toBeInTheDocument(); + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + }); + + it('should remove warning text when removing `event.category`', async () => { + mockedContext.setExperimentalFlag({ + filterProcessDescendantsForEventFiltersEnabled: true, + }); + + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'some value 6', + }, + ]; + formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG]; + + render(); + + expect( + await renderResult.findByTestId('duplicate-fields-warning-message') + ).toBeInTheDocument(); + + // switch to classic Event filtering + userEvent.click(renderResult.getByTestId(`builderItemEntryDeleteButton`)); + rerenderWithLatestProps(); + + expect( + renderResult.queryByTestId('duplicate-fields-warning-message') + ).not.toBeInTheDocument(); + }); + }); }); - it('should not show warning callout when wildcard is used with the "MATCHES" operator', async () => { - formProps.item.entries = [ - { - field: 'event.category', - operator: 'included', - type: 'wildcard', - value: 'valuewithwildcard*', - }, - ]; - rerender(); - expect(renderResult.queryByTestId('wildcardWithWrongOperatorCallout')).toBeNull(); - }); - it('should show warning callout when wildcard is used with the "IS" operator', async () => { - formProps.item.entries = [ - { - field: 'event.category', - operator: 'included', - type: 'match', - value: 'valuewithwildcard*', - }, - ]; - rerender(); - await expect(renderResult.findByTestId('wildcardWithWrongOperatorCallout')).not.toBeNull(); + describe('wildcard with wrong operator', () => { + it('should not show warning callout when wildcard is used with the "MATCHES" operator', async () => { + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'wildcard', + value: 'valuewithwildcard*', + }, + ]; + render(); + expect(await renderResult.findByDisplayValue('valuewithwildcard*')).toBeInTheDocument(); + + expect( + renderResult.queryByTestId('wildcardWithWrongOperatorCallout') + ).not.toBeInTheDocument(); + }); + + it('should show warning callout when wildcard is used with the "IS" operator', async () => { + formProps.item.entries = [ + { + field: 'event.category', + operator: 'included', + type: 'match', + value: 'valuewithwildcard*', + }, + ]; + render(); + + expect( + await renderResult.findByTestId('wildcardWithWrongOperatorCallout') + ).toBeInTheDocument(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx index 4275502961848..afdab70202f12 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx @@ -41,6 +41,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use import { useGetUpdatedTags } from '../../../../hooks/artifacts'; import { FILTER_PROCESS_DESCENDANTS_TAG, + PROCESS_DESCENDANT_EVENT_FILTER_EXTRA_ENTRY, PROCESS_DESCENDANT_EVENT_FILTER_EXTRA_ENTRY_TEXT, } from '../../../../../../common/endpoint/service/artifacts/constants'; import { @@ -545,11 +546,19 @@ export const EventFiltersForm: React.FC e.field) || ['']; + + if (isFilterProcessDescendantsFeatureEnabled && isFilterProcessDescendantsSelected) { + addedFields.push(PROCESS_DESCENDANT_EVENT_FILTER_EXTRA_ENTRY.field); + } + setHasDuplicateFields(computeHasDuplicateFields(getAddedFieldsCounts(addedFields))); if (!hasFormChanged) setHasFormChanged(true); return; + } else { + setHasDuplicateFields(false); } // handle wildcard with wrong operator case @@ -576,7 +585,13 @@ export const EventFiltersForm: React.FC diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx index 43afb4bd9054f..e49385ccc7eca 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx @@ -20,8 +20,6 @@ import { MANAGEMENT_PATH } from '../../../../../common/constants'; import { getActionListMock } from '../../../components/endpoint_response_actions_list/mocks'; import { useGetEndpointsList } from '../../../hooks/endpoint/use_get_endpoints_list'; -jest.mock('../../../../common/experimental_features_service'); - let mockUseGetEndpointActionList: { isFetched?: boolean; isFetching?: boolean; @@ -463,7 +461,12 @@ describe('Response actions history page', () => { expect(history.location.search).toEqual('?page=1&pageSize=20'); }); - it('should set selected command filter options to URL params ', () => { + // TODO: remove this test when responseActionScanEnabled is removed + it('should set selected command filter options to URL params (without `responseActionScanEnabled`)', () => { + mockedContext.setExperimentalFlag({ + responseActionScanEnabled: false, + }); + const filterPrefix = 'actions-filter'; render(); const { getAllByTestId, getByTestId } = renderResult; @@ -480,6 +483,27 @@ describe('Response actions history page', () => { ); }); + it('should set selected command filter options to URL params (with `responseActionScanEnabled`)', () => { + mockedContext.setExperimentalFlag({ + responseActionScanEnabled: true, + }); + + const filterPrefix = 'actions-filter'; + render(); + const { getAllByTestId, getByTestId } = renderResult; + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const allFilterOptions = getAllByTestId(`${filterPrefix}-option`); + + allFilterOptions.forEach((option) => { + option.style.pointerEvents = 'all'; + userEvent.click(option); + }); + + expect(history.location.search).toEqual( + '?commands=isolate%2Crelease%2Ckill-process%2Csuspend-process%2Cprocesses%2Cget-file%2Cexecute%2Cupload%2Cscan' + ); + }); + it('should set selected hosts filter options to URL params ', () => { const filterPrefix = 'hosts-filter'; render(); @@ -607,7 +631,12 @@ describe('Response actions history page', () => { }); describe('Clear all selected options on a filter', () => { - it('should clear all selected options on `actions` filter', () => { + // TODO: remove this test when responseActionScanEnabled is removed + it('should clear all selected options on `actions` filter (without `responseActionScanEnabled`)', () => { + mockedContext.setExperimentalFlag({ + responseActionScanEnabled: false, + }); + const filterPrefix = 'actions-filter'; render(); const { getAllByTestId, getByTestId } = renderResult; @@ -629,6 +658,32 @@ describe('Response actions history page', () => { expect(history.location.search).toEqual(''); }); + it('should clear all selected options on `actions` filter (with `responseActionScanEnabled`)', () => { + mockedContext.setExperimentalFlag({ + responseActionScanEnabled: true, + }); + + const filterPrefix = 'actions-filter'; + render(); + const { getAllByTestId, getByTestId } = renderResult; + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const allFilterOptions = getAllByTestId(`${filterPrefix}-option`); + + allFilterOptions.forEach((option) => { + option.style.pointerEvents = 'all'; + userEvent.click(option); + }); + + expect(history.location.search).toEqual( + '?commands=isolate%2Crelease%2Ckill-process%2Csuspend-process%2Cprocesses%2Cget-file%2Cexecute%2Cupload%2Cscan' + ); + + const clearAllButton = getByTestId(`${testPrefix}-${filterPrefix}-clearAllButton`); + clearAllButton.style.pointerEvents = 'all'; + userEvent.click(clearAllButton); + expect(history.location.search).toEqual(''); + }); + it('should clear all selected options on `hosts` filter', () => { const filterPrefix = 'hosts-filter'; render(); diff --git a/x-pack/plugins/security_solution/public/notes/components/open_event_in_timeline.tsx b/x-pack/plugins/security_solution/public/notes/components/open_event_in_timeline.tsx new file mode 100644 index 0000000000000..43f039836ccad --- /dev/null +++ b/x-pack/plugins/security_solution/public/notes/components/open_event_in_timeline.tsx @@ -0,0 +1,24 @@ +/* + * 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 React, { memo } from 'react'; +import { EuiLink } from '@elastic/eui'; +import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { useInvestigateInTimeline } from '../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline'; +import * as i18n from './translations'; + +export const OpenEventInTimeline: React.FC<{ eventId?: string | null }> = memo(({ eventId }) => { + const ecsRowData = { event: { id: [eventId] }, _id: eventId } as Ecs; + const { investigateInTimelineAlertClick } = useInvestigateInTimeline({ ecsRowData }); + + return ( + + {i18n.VIEW_EVENT_IN_TIMELINE} + + ); +}); + +OpenEventInTimeline.displayName = 'OpenEventInTimeline'; diff --git a/x-pack/plugins/security_solution/public/notes/components/search_row.tsx b/x-pack/plugins/security_solution/public/notes/components/search_row.tsx index 1e88a47b2e2d2..f2e86d324a54a 100644 --- a/x-pack/plugins/security_solution/public/notes/components/search_row.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/search_row.tsx @@ -7,9 +7,9 @@ import { EuiFlexGroup, EuiFlexItem, EuiSearchBar } from '@elastic/eui'; import React, { useMemo, useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import styled from 'styled-components'; -import { userSearchedNotes, selectNotesTableSearch } from '..'; +import { userSearchedNotes } from '..'; const SearchRowContainer = styled.div` &:not(:last-child) { @@ -36,8 +36,6 @@ export const SearchRow = React.memo(() => { [] ); - const notesSearch = useSelector(selectNotesTableSearch); - const onQueryChange = useCallback( ({ queryText }) => { dispatch(userSearchedNotes(queryText.trim())); @@ -49,12 +47,7 @@ export const SearchRow = React.memo(() => { - + diff --git a/x-pack/plugins/security_solution/public/notes/components/translations.ts b/x-pack/plugins/security_solution/public/notes/components/translations.ts index 471c28cbc9d4c..f2846d6daab62 100644 --- a/x-pack/plugins/security_solution/public/notes/components/translations.ts +++ b/x-pack/plugins/security_solution/public/notes/components/translations.ts @@ -31,14 +31,14 @@ export const CREATED_BY_COLUMN = i18n.translate( export const EVENT_ID_COLUMN = i18n.translate( 'xpack.securitySolution.notes.management.eventIdColumnTitle', { - defaultMessage: 'Document ID', + defaultMessage: 'View Document', } ); export const TIMELINE_ID_COLUMN = i18n.translate( - 'xpack.securitySolution.notes.management.timelineIdColumnTitle', + 'xpack.securitySolution.notes.management.timelineColumnTitle', { - defaultMessage: 'Timeline ID', + defaultMessage: 'Timeline', } ); @@ -60,13 +60,6 @@ export const DELETE_SINGLE_NOTE_DESCRIPTION = i18n.translate( } ); -export const NOTES_MANAGEMENT_TITLE = i18n.translate( - 'xpack.securitySolution.notes.management.pageTitle', - { - defaultMessage: 'Notes management', - } -); - export const TABLE_ERROR = i18n.translate('xpack.securitySolution.notes.management.tableError', { defaultMessage: 'Unable to load notes', }); @@ -102,3 +95,17 @@ export const DELETE_SELECTED = i18n.translate( export const REFRESH = i18n.translate('xpack.securitySolution.notes.management.refresh', { defaultMessage: 'Refresh', }); + +export const OPEN_TIMELINE = i18n.translate( + 'xpack.securitySolution.notes.management.openTimeline', + { + defaultMessage: 'Open timeline', + } +); + +export const VIEW_EVENT_IN_TIMELINE = i18n.translate( + 'xpack.securitySolution.notes.management.viewEventInTimeline', + { + defaultMessage: 'View event in timeline', + } +); diff --git a/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx b/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx index 1c39265a1b02f..a951968612ba7 100644 --- a/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx +++ b/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useMemo, useEffect } from 'react'; import type { DefaultItemAction, EuiBasicTableColumn } from '@elastic/eui'; -import { EuiBasicTable, EuiEmptyPrompt } from '@elastic/eui'; +import { EuiBasicTable, EuiEmptyPrompt, EuiLink } from '@elastic/eui'; import { useDispatch, useSelector } from 'react-redux'; // TODO unify this type from the api with the one in public/common/lib/note import type { Note } from '../../../common/api/timeline'; @@ -33,41 +33,58 @@ import { SearchRow } from '../components/search_row'; import { NotesUtilityBar } from '../components/utility_bar'; import { DeleteConfirmModal } from '../components/delete_confirm_modal'; import * as i18n from '../components/translations'; +import type { OpenTimelineProps } from '../../timelines/components/open_timeline/types'; +import { OpenEventInTimeline } from '../components/open_event_in_timeline'; + +const columns: ( + onOpenTimeline: OpenTimelineProps['onOpenTimeline'] +) => Array> = (onOpenTimeline) => { + return [ + { + field: 'created', + name: i18n.CREATED_COLUMN, + sortable: true, + render: (created: Note['created']) => , + }, + { + field: 'createdBy', + name: i18n.CREATED_BY_COLUMN, + }, + { + field: 'eventId', + name: i18n.EVENT_ID_COLUMN, + sortable: true, + render: (eventId: Note['eventId']) => , + }, + { + field: 'timelineId', + name: i18n.TIMELINE_ID_COLUMN, + render: (timelineId: Note['timelineId']) => + timelineId ? ( + onOpenTimeline({ timelineId, duplicate: false })}> + {i18n.OPEN_TIMELINE} + + ) : null, + }, + { + field: 'note', + name: i18n.NOTE_CONTENT_COLUMN, + }, + ]; +}; -const columns: Array> = [ - { - field: 'created', - name: i18n.CREATED_COLUMN, - sortable: true, - render: (created: Note['created']) => , - }, - { - field: 'createdBy', - name: i18n.CREATED_BY_COLUMN, - }, - { - field: 'eventId', - name: i18n.EVENT_ID_COLUMN, - sortable: true, - }, - { - field: 'timelineId', - name: i18n.TIMELINE_ID_COLUMN, - }, - { - field: 'note', - name: i18n.NOTE_CONTENT_COLUMN, - }, -]; - -const pageSizeOptions = [50, 25, 10, 0]; +const pageSizeOptions = [10, 25, 50, 100]; /** * Allows user to search and delete notes. * This component uses the same slices of state as the notes functionality of the rest of the Security Solution applicaiton. * Therefore, changes made in this page (like fetching or deleting notes) will have an impact everywhere. */ -export const NoteManagementPage = () => { +export const NoteManagementPage = ({ + onOpenTimeline, +}: { + onOpenTimeline: OpenTimelineProps['onOpenTimeline']; +}) => { const dispatch = useDispatch(); const notes = useSelector(selectAllNotes); const pagination = useSelector(selectNotesPagination); @@ -147,13 +164,13 @@ export const NoteManagementPage = () => { }, ]; return [ - ...columns, + ...columns(onOpenTimeline), { name: 'actions', actions, }, ]; - }, [selectRowForDeletion]); + }, [selectRowForDeletion, onOpenTimeline]); const currentPagination = useMemo(() => { return { diff --git a/x-pack/plugins/security_solution/public/overview/components/common.tsx b/x-pack/plugins/security_solution/public/overview/components/common.tsx index fcf4072db1afd..078b2bbe17fa2 100644 --- a/x-pack/plugins/security_solution/public/overview/components/common.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/common.tsx @@ -8,14 +8,14 @@ import { EuiButtonIcon, EuiPopover, EuiPopoverTitle, EuiText } from '@elastic/eui'; import React, { useCallback, useState } from 'react'; import * as i18n from './translations'; -import { RiskScoreDocLink } from '../../entity_analytics/components/risk_score_onboarding/risk_score_doc_link'; -import type { RiskScoreEntity } from '../../../common/entity_analytics/risk_engine'; +import { EntityAnalyticsLearnMoreLink } from '../../entity_analytics/components/risk_score_onboarding/entity_analytics_doc_link'; export const RiskScoreInfoTooltip: React.FC<{ toolTipContent: React.ReactNode; toolTipTitle?: React.ReactNode; width?: number; -}> = ({ toolTipContent, toolTipTitle, width = 270 }) => { + anchorPosition?: EuiPopover['props']['anchorPosition']; +}> = ({ toolTipContent, toolTipTitle, width = 270, anchorPosition = 'leftCenter' }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const onClick = useCallback(() => { @@ -30,7 +30,7 @@ export const RiskScoreInfoTooltip: React.FC<{ ( +export const RiskScoreDocTooltip = ({ + anchorPosition, +}: { + anchorPosition?: React.ComponentProps['anchorPosition']; +}) => ( } - width={200} // Magic number to match the width of the doc link + anchorPosition={anchorPosition} + toolTipContent={} /> ); diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx index 6ac68b9522762..b25509c1b88f5 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx @@ -158,7 +158,7 @@ export const HostOverview = React.memo( /> - + ), diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx index 30e528509b9c1..102e39cf1b740 100644 --- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx @@ -156,7 +156,7 @@ export const UserOverview = React.memo( /> - + ), diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx index 3dc686229e4fa..015a36717475e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx @@ -312,7 +312,7 @@ export const OpenTimeline = React.memo( /> ) : ( - + )}
    diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx index 155c92403af3b..b6df692dcabfd 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_row_renderer/index.tsx @@ -6,7 +6,13 @@ */ import { noop } from 'lodash/fp'; -import { EuiFocusTrap, EuiOutsideClickDetector, EuiScreenReaderOnly } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFocusTrap, + EuiOutsideClickDetector, + EuiScreenReaderOnly, + EuiFlexItem, +} from '@elastic/eui'; import React, { useMemo } from 'react'; import { @@ -73,13 +79,15 @@ export const StatefulRowRenderer = ({

    {i18n.YOU_ARE_IN_AN_EVENT_RENDERER(ariaRowindex)}

    -
    - {rowRenderer.renderRow({ - data: event.ecs, - isDraggable: true, - scopeId: timelineId, - })} -
    + + + {rowRenderer.renderRow({ + data: event.ecs, + isDraggable: true, + scopeId: timelineId, + })} + +
    diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx index 576812016dee8..d832eee22bb64 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/unified_timeline_body.tsx @@ -9,7 +9,7 @@ import type { ComponentProps, ReactElement } from 'react'; import React, { useEffect, useState, useMemo } from 'react'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; import { isEmpty } from 'lodash'; -import { StyledTableFlexGroup, StyledTableFlexItem } from '../unified_components/styles'; +import { StyledTableFlexGroup, StyledUnifiedTableFlexItem } from '../unified_components/styles'; import { UnifiedTimeline } from '../unified_components'; import { defaultUdtHeaders } from '../unified_components/default_headers'; import type { PaginationInputPaginated, TimelineItem } from '../../../../../common/search_strategy'; @@ -65,8 +65,11 @@ export const UnifiedTimelineBody = (props: UnifiedTimelineBodyProps) => { return ( - {header} - + {header} + { leadingControlColumns={leadingControlColumns} /> - + ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/assistant/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/assistant/index.tsx deleted file mode 100644 index a71b7c8231f59..0000000000000 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/assistant/index.tsx +++ /dev/null @@ -1,35 +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 styled from 'styled-components'; -import { Assistant } from '@kbn/elastic-assistant'; -import type { Dispatch, SetStateAction } from 'react'; -import React, { memo } from 'react'; -import { TIMELINE_CONVERSATION_TITLE } from '../../../../../assistant/content/conversations/translations'; - -const AssistantTabContainer = styled.div` - overflow-y: auto; - width: 100%; -`; - -const AssistantTab: React.FC<{ - shouldRefocusPrompt: boolean; - setConversationTitle: Dispatch>; -}> = memo(({ shouldRefocusPrompt, setConversationTitle }) => ( - - - -)); - -AssistantTab.displayName = 'AssistantTab'; - -// eslint-disable-next-line import/no-default-export -export { AssistantTab as default }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx index 0d6332ffe805c..7492a00a4a2de 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx @@ -285,31 +285,29 @@ export const EqlTabContentComponent: React.FC = ({ {NotesFlyoutMemo} - - - + ) : ( @@ -325,14 +323,9 @@ export const EqlTabContentComponent: React.FC = ({ loading={isQueryLoading} refetch={refetch} /> - - - + + {unifiedHeader} + >; }; const ActiveTimelineTab = memo( @@ -106,10 +100,8 @@ const ActiveTimelineTab = memo( rowRenderers, timelineId, timelineType, - setConversationTitle, showTimeline, }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); const { isTimelineEsqlEnabledByFeatureFlag, isEsqlAdvancedSettingEnabled } = useEsqlAvailability(); const timelineESQLSavedSearch = useShallowEqualSelector((state) => @@ -124,7 +116,6 @@ const ActiveTimelineTab = memo( } return isEsqlAdvancedSettingEnabled || timelineESQLSavedSearch != null; }, [isEsqlAdvancedSettingEnabled, isTimelineEsqlEnabledByFeatureFlag, timelineESQLSavedSearch]); - const aiAssistantFlyoutMode = useIsExperimentalFeatureEnabled('aiAssistantFlyoutMode'); const getTab = useCallback( (tab: TimelineTabs) => { switch (tab) { @@ -147,33 +138,6 @@ const ActiveTimelineTab = memo( [activeTimelineTab] ); - const getAssistantTab = useCallback(() => { - if (showTimeline) { - const AssistantTab = tabWithSuspense(lazy(() => import('./assistant'))); - return ( - - ); - } else { - return null; - } - }, [activeTimelineTab, setConversationTitle, showTimeline]); - /* Future developer -> why are we doing that * It is really expansive to re-render the QueryTab because the drag/drop * Therefore, we are only hiding its dom when switching to another tab @@ -228,7 +192,6 @@ const ActiveTimelineTab = memo( > {isGraphOrNotesTabs && getTab(activeTimelineTab)} - {hasAssistantPrivilege && !aiAssistantFlyoutMode ? getAssistantTab() : null} ); } @@ -271,8 +234,6 @@ const TabsContentComponent: React.FC = ({ sessionViewConfig, timelineDescription, }) => { - const aiAssistantFlyoutMode = useIsExperimentalFeatureEnabled('aiAssistantFlyoutMode'); - const { hasAssistantPrivilege } = useAssistantAvailability(); const dispatch = useDispatch(); const getActiveTab = useMemo(() => getActiveTabSelector(), []); const getShowTimeline = useMemo(() => getShowTimelineSelector(), []); @@ -312,9 +273,6 @@ const TabsContentComponent: React.FC = ({ const isEnterprisePlus = useLicense().isEnterprise(); - const [conversationTitle, setConversationTitle] = useState(TIMELINE_CONVERSATION_TITLE); - const { reportAssistantInvoked } = useAssistantTelemetry(); - const allTimelineNoteIds = useMemo(() => { const eventNoteIds = Object.values(eventIdToNoteIds).reduce( (acc, v) => [...acc, ...v], @@ -361,16 +319,6 @@ const TabsContentComponent: React.FC = ({ setActiveTab(TimelineTabs.session); }, [setActiveTab]); - const setSecurityAssistantAsActiveTab = useCallback(() => { - setActiveTab(TimelineTabs.securityAssistant); - if (activeTab !== TimelineTabs.securityAssistant) { - reportAssistantInvoked({ - conversationId: conversationTitle, - invokedBy: TIMELINE_CONVERSATION_TITLE, - }); - } - }, [activeTab, conversationTitle, reportAssistantInvoked, setActiveTab]); - const setEsqlAsActiveTab = useCallback(() => { dispatch( initializeTimelineSettings({ @@ -471,17 +419,6 @@ const TabsContentComponent: React.FC = ({
    )} - {hasAssistantPrivilege && !aiAssistantFlyoutMode && ( - - {i18n.SECURITY_ASSISTANT} - - )} )} @@ -492,7 +429,6 @@ const TabsContentComponent: React.FC = ({ timelineId={timelineId} timelineType={timelineType} timelineDescription={timelineDescription} - setConversationTitle={setConversationTitle} showTimeline={showTimeline} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/layout.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/layout.tsx index f3a8981e70ad2..aacb38ea2e798 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/layout.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/layout.tsx @@ -16,7 +16,6 @@ import { import styled from 'styled-components'; export const TabHeaderContainer = styled.div` - margin-top: ${(props) => props.theme.eui.euiSizeS}; width: 100%; `; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap index 4e220a4d2db51..b3e43da0eb8a1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/__snapshots__/custom_timeline_data_grid_body.test.tsx.snap @@ -75,7 +75,7 @@ exports[`CustomTimelineDataGridBody should render exactly as snapshots 1`] = `
    diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/styles.tsx index 78ab042155e7e..2d32767ea19c0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/styles.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/styles.tsx @@ -22,7 +22,7 @@ export const StyledTableFlexGroup = styled(EuiFlexGroup).attrs(({ className = '' } `; -export const StyledTableFlexItem = styled(EuiFlexItem).attrs(({ className = '' }) => ({ +export const StyledUnifiedTableFlexItem = styled(EuiFlexItem).attrs(({ className = '' }) => ({ className: `${className}`, }))` ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} diff --git a/x-pack/plugins/security_solution/public/timelines/links.ts b/x-pack/plugins/security_solution/public/timelines/links.ts index 97667c0ce8aa3..169ef6da01910 100644 --- a/x-pack/plugins/security_solution/public/timelines/links.ts +++ b/x-pack/plugins/security_solution/public/timelines/links.ts @@ -37,8 +37,6 @@ export const links: LinkItem = { defaultMessage: 'Visualize and delete notes.', }), path: `${TIMELINES_PATH}/notes`, - skipUrlState: true, - hideTimeline: true, experimentalKey: 'securitySolutionNotesEnabled', }, ], diff --git a/x-pack/plugins/security_solution/public/timelines/routes.tsx b/x-pack/plugins/security_solution/public/timelines/routes.tsx index a7d6373007192..e7f28eb9d71e3 100644 --- a/x-pack/plugins/security_solution/public/timelines/routes.tsx +++ b/x-pack/plugins/security_solution/public/timelines/routes.tsx @@ -5,39 +5,15 @@ * 2.0. */ -import React, { memo } from 'react'; +import React from 'react'; import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; -import { Switch } from 'react-router-dom'; -import { Route } from '@kbn/shared-ux-router'; -import { SpyRoute } from '../common/utils/route/spy_routes'; -import { NotFoundPage } from '../app/404'; -import { NoteManagementPage } from '../notes/pages/note_management_page'; import { PluginTemplateWrapper } from '../common/components/plugin_template_wrapper'; import { SecurityPageName } from '../app/types'; import type { SecuritySubPluginRoutes } from '../app/types'; -import { NOTES_MANAGEMENT_PATH, TIMELINES_PATH } from '../../common/constants'; +import { TIMELINES_PATH } from '../../common/constants'; import { Timelines } from './pages'; -const NoteManagementTelemetry = () => ( - - - - - - -); - -const NoteManagementContainer = memo(() => { - return ( - - - - - ); -}); -NoteManagementContainer.displayName = 'NoteManagementContainer'; - const TimelinesRoutes = () => ( @@ -51,8 +27,4 @@ export const routes: SecuritySubPluginRoutes = [ path: TIMELINES_PATH, component: TimelinesRoutes, }, - { - path: NOTES_MANAGEMENT_PATH, - component: NoteManagementContainer, - }, ]; diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 6f80a018319fa..d7181b3ce49c6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -11,10 +11,10 @@ import type { Logger, LoggerFactory, SavedObjectsClientContract, + SecurityServiceStart, } from '@kbn/core/server'; import type { ExceptionListClient, ListsServerExtensionRegistrar } from '@kbn/lists-plugin/server'; import type { CasesClient, CasesServerStart } from '@kbn/cases-plugin/server'; -import type { SecurityPluginStart } from '@kbn/security-plugin/server'; import type { FleetFromHostFileClientInterface, FleetStartContract, @@ -68,7 +68,7 @@ export interface EndpointAppContextServiceStartContract { endpointMetadataService: EndpointMetadataService; endpointFleetServicesFactory: EndpointFleetServicesFactoryInterface; manifestManager?: ManifestManager; - security: SecurityPluginStart; + security: SecurityServiceStart; alerting: AlertsPluginStartContract; config: ConfigType; registerIngestCallback?: FleetStartContract['registerExternalCallback']; @@ -93,7 +93,7 @@ export class EndpointAppContextService { private setupDependencies: EndpointAppContextServiceSetupContract | null = null; private startDependencies: EndpointAppContextServiceStartContract | null = null; private fleetServicesFactory: EndpointFleetServicesFactoryInterface | null = null; - public security: SecurityPluginStart | undefined; + public security: SecurityServiceStart | undefined; public setup(dependencies: EndpointAppContextServiceSetupContract) { this.setupDependencies = dependencies; diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts index dbc57c2a55a84..0207a3b5d1460 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/complete_external_actions_task_runner.ts @@ -152,7 +152,7 @@ export class CompleteExternalActionsTaskRunner return null; } - this.errors.push(err.message); + this.errors.push(err.stack); }); } ) @@ -164,7 +164,7 @@ export class CompleteExternalActionsTaskRunner if (this.errors.length) { this.log.error( `${this.errors.length} errors were encountered while running task:\n${this.errors.join( - '\n' + '\n----' )}` ); } diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts index 3cd77526942ff..9b6f001934910 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks/mocks.ts @@ -15,6 +15,7 @@ import { loggingSystemMock, savedObjectsClientMock, savedObjectsServiceMock, + securityServiceMock, } from '@kbn/core/server/mocks'; import type { IRouter, @@ -154,7 +155,7 @@ export const createMockEndpointAppContextServiceStartContract = const logger = loggingSystemMock.create().get('mock_endpoint_app_context'); const savedObjectsStart = savedObjectsServiceMock.createStartContract(); - const security = securityMock.createStart(); + const security = securityServiceMock.createStart(); const agentService = createMockAgentService(); const agentPolicyService = createMockAgentPolicyService(); const packagePolicyService = createPackagePolicyServiceMock(); @@ -196,10 +197,6 @@ export const createMockEndpointAppContextServiceStartContract = securityMock.createMockAuthenticatedUser({ roles: ['superuser'] }) ); - security.authz.checkPrivilegesDynamicallyWithRequest.mockReturnValue( - jest.fn(() => ({ privileges: { kibana: [] } })) - ); - const casesMock = casesPluginMock.createStartContract(); const fleetActionsClientMock = createFleetActionsClientMock(); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts index cb4afe4496472..0037d5dded81f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts @@ -66,11 +66,12 @@ export const getActionFileDownloadRouteHandler = ( return async (context, req, res) => { const { action_id: actionId, file_id: fileId } = req.params; + const coreContext = await context.core; try { - const esClient = (await context.core).elasticsearch.client.asInternalUser; + const esClient = coreContext.elasticsearch.client.asInternalUser; const { agentType } = await getActionAgentType(esClient, actionId); - const user = endpointContext.service.security?.authc.getCurrentUser(req); + const user = coreContext.security.authc.getCurrentUser(); const casesClient = await endpointContext.service.getCasesClient(req); const connectorActions = (await context.actions).getActionsClient(); const responseActionsClient: ResponseActionsClient = getResponseActionsClient(agentType, { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts index 0576cec69b01c..4b1d67ff88718 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts @@ -36,11 +36,12 @@ export const getActionFileInfoRouteHandler = ( return async (context, req, res) => { const { action_id: requestActionId, file_id: fileId } = req.params; + const coreContext = await context.core; try { - const esClient = (await context.core).elasticsearch.client.asInternalUser; + const esClient = coreContext.elasticsearch.client.asInternalUser; const { agentType } = await getActionAgentType(esClient, requestActionId); - const user = endpointContext.service.security?.authc.getCurrentUser(req); + const user = coreContext.security.authc.getCurrentUser(); const casesClient = await endpointContext.service.getCasesClient(req); const connectorActions = (await context.actions).getActionsClient(); const responseActionsClient: ResponseActionsClient = getResponseActionsClient(agentType, { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 8c1ba19ca626d..47099ae060efa 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -192,11 +192,6 @@ describe('Response actions', () => { }: CallRouteInterface, indexExists?: { endpointDsExists: boolean } ): Promise> => { - const asUser = mockUser ? mockUser : superUser; - (startContract.security.authc.getCurrentUser as jest.Mock).mockImplementationOnce( - () => asUser - ); - const ctx = createRouteHandlerContext(mockScopedClient, mockSavedObjectClient); ctx.securitySolution.getEndpointAuthz.mockResolvedValue( @@ -218,6 +213,9 @@ describe('Response actions', () => { }; } ); + const asUser = mockUser ? mockUser : superUser; + (ctx.core.security.authc.getCurrentUser as jest.Mock).mockImplementationOnce(() => asUser); + const metadataResponse = docGen.generateHostMetadata(); const withErrorResponse = indexErrorResponse ? indexErrorResponse : { statusCode: 201 }; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts index 2bbe35f9747b4..723daf576c38e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts @@ -47,10 +47,11 @@ import { import type { ActionDetails, EndpointActionDataParameterTypes, - KillOrSuspendProcessRequestBody, - ResponseActionParametersWithPidOrEntityId, + ResponseActionParametersWithProcessData, ResponseActionsExecuteParameters, ResponseActionScanParameters, + KillProcessRequestBody, + SuspendProcessRequestBody, } from '../../../../common/endpoint/types'; import type { ResponseActionsApiCommandNames } from '../../../../common/endpoint/service/response_actions/constants'; import type { @@ -165,7 +166,7 @@ export function registerResponseActionRoutes( withEndpointAuthz( { all: ['canKillProcess'] }, logger, - responseActionRequestHandler( + responseActionRequestHandler( endpointContext, 'kill-process' ) @@ -188,7 +189,7 @@ export function registerResponseActionRoutes( withEndpointAuthz( { all: ['canSuspendProcess'] }, logger, - responseActionRequestHandler( + responseActionRequestHandler( endpointContext, 'suspend-process' ) @@ -337,8 +338,9 @@ function responseActionRequestHandler { subActionParams: { actionParameters: { comment: - 'Action triggered from Elastic Security by user foo for action 123-345-456: test comment', + 'Action triggered from Elastic Security by user [foo] for action [isolate (action id: 123-345-456)]: test comment', }, command: 'contain', ids: ['1-2-3'], @@ -232,7 +232,7 @@ describe('CrowdstrikeActionsClient class', () => { command: 'lift_containment', ids: ['1-2-3'], comment: - 'Action triggered from Elastic Security by user foo for action 123-345-456: test comment', + 'Action triggered from Elastic Security by user [foo] for action [unisolate (action id: 123-345-456)]: test comment', }, }, }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts index 72994508ebc22..958c51014c6a0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts @@ -12,6 +12,7 @@ import { } from '@kbn/stack-connectors-plugin/common/crowdstrike/constants'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import type { CrowdstrikeBaseApiResponse } from '@kbn/stack-connectors-plugin/common/crowdstrike/types'; +import { v4 as uuidv4 } from 'uuid'; import type { CrowdstrikeActionRequestCommonMeta } from '../../../../../../common/endpoint/types/crowdstrike'; import type { CommonResponseActionMethodOptions, @@ -37,7 +38,6 @@ import type { NormalizedExternalConnectorClient, NormalizedExternalConnectorClientExecuteOptions, } from '../lib/normalized_external_connector_client'; -import { ELASTIC_RESPONSE_ACTION_MESSAGE } from '../../utils'; export type CrowdstrikeActionsClientOptions = ResponseActionsClientOptions & { connectorActions: NormalizedExternalConnectorClient; @@ -190,19 +190,15 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl { let actionResponse: ActionTypeExecutorResult | undefined; if (!reqIndexOptions.error) { let error = (await this.validateRequest(reqIndexOptions)).error; - const actionCommentMessage = ELASTIC_RESPONSE_ACTION_MESSAGE( - this.options.username, - reqIndexOptions.actionId - ); if (!error) { + if (!reqIndexOptions.actionId) { + reqIndexOptions.actionId = uuidv4(); + } + try { actionResponse = (await this.sendAction(SUB_ACTION.HOST_ACTIONS, { ids: actionRequest.endpoint_ids, - actionParameters: { - comment: reqIndexOptions.comment - ? `${actionCommentMessage}: ${reqIndexOptions.comment}` - : actionCommentMessage, - }, + actionParameters: { comment: this.buildExternalComment(reqIndexOptions) }, command: 'contain', })) as ActionTypeExecutorResult; } catch (err) { @@ -254,18 +250,13 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl { let actionResponse: ActionTypeExecutorResult | undefined; if (!reqIndexOptions.error) { let error = (await this.validateRequest(reqIndexOptions)).error; - const actionCommentMessage = ELASTIC_RESPONSE_ACTION_MESSAGE( - this.options.username, - reqIndexOptions.actionId - ); + if (!error) { try { actionResponse = (await this.sendAction(SUB_ACTION.HOST_ACTIONS, { ids: actionRequest.endpoint_ids, command: 'lift_containment', - comment: reqIndexOptions.comment - ? `${actionCommentMessage}: ${reqIndexOptions.comment}` - : actionCommentMessage, + comment: this.buildExternalComment(reqIndexOptions), })) as ActionTypeExecutorResult; } catch (err) { error = err; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts index 1fa046496f231..21a4196ba84e5 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts @@ -263,7 +263,8 @@ describe('EndpointActionsClient', () => { }); }); - it('should create an action with error', async () => { + it('should create an action with error and not trow when in automated mode', async () => { + classConstructorOptions.isAutomated = true; await endpointActionsClient.isolate(getCommonResponseActionOptions(), { error: 'something is wrong', }); @@ -283,7 +284,8 @@ describe('EndpointActionsClient', () => { ); }); - it('should create an action with error when agents are invalid', async () => { + it('should create an action with error when agents are invalid (automated mode)', async () => { + classConstructorOptions.isAutomated = true; // @ts-expect-error mocking this for testing purposes endpointActionsClient.checkAgentIds = jest.fn().mockResolvedValueOnce({ isValid: false, diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts index 2c908bd1a3f30..59328beb46c12 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts @@ -30,12 +30,11 @@ import type { ActionDetails, HostMetadata, GetProcessesActionOutputContent, - KillOrSuspendProcessRequestBody, KillProcessActionOutputContent, ResponseActionExecuteOutputContent, ResponseActionGetFileOutputContent, ResponseActionGetFileParameters, - ResponseActionParametersWithPidOrEntityId, + ResponseActionParametersWithProcessData, ResponseActionsExecuteParameters, ResponseActionUploadOutputContent, ResponseActionUploadParameters, @@ -45,6 +44,8 @@ import type { UploadedFileInfo, ResponseActionScanParameters, ResponseActionScanOutputContent, + KillProcessRequestBody, + SuspendProcessRequestBody, } from '../../../../../../common/endpoint/types'; import type { CommonResponseActionMethodOptions, @@ -107,6 +108,10 @@ export class EndpointActionsClient extends ResponseActionsClientImpl { const { hosts, ruleName, ruleId, error } = this.getMethodOptions(options); let actionError: string | undefined = validationError?.message || error; + if (actionError && !this.options.isAutomated) { + throw new ResponseActionsClientError(actionError, 400); + } + // Dispatch action to Endpoint using Fleet if (!actionError) { try { @@ -242,26 +247,26 @@ export class EndpointActionsClient extends ResponseActionsClientImpl { } async killProcess( - actionRequest: KillOrSuspendProcessRequestBody, + actionRequest: KillProcessRequestBody, options: CommonResponseActionMethodOptions = {} ): Promise< - ActionDetails + ActionDetails > { return this.handleResponseAction< - KillOrSuspendProcessRequestBody, - ActionDetails + KillProcessRequestBody, + ActionDetails >('kill-process', actionRequest, options); } async suspendProcess( - actionRequest: KillOrSuspendProcessRequestBody, + actionRequest: SuspendProcessRequestBody, options: CommonResponseActionMethodOptions = {} ): Promise< - ActionDetails + ActionDetails > { return this.handleResponseAction< - KillOrSuspendProcessRequestBody, - ActionDetails + SuspendProcessRequestBody, + ActionDetails >('suspend-process', actionRequest, options); } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts index 677165519796b..927875855eb11 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts @@ -13,6 +13,7 @@ import { AttachmentType, ExternalReferenceStorageType } from '@kbn/cases-plugin/ import type { CaseAttachments } from '@kbn/cases-plugin/public/types'; import { i18n } from '@kbn/i18n'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { SimpleMemCache } from './simple_mem_cache'; import { validateActionId } from '../../utils/validate_action_id'; import { fetchActionResponses, @@ -49,14 +50,13 @@ import type { EndpointActionDataParameterTypes, EndpointActionResponseDataOutput, GetProcessesActionOutputContent, - KillOrSuspendProcessRequestBody, KillProcessActionOutputContent, LogsEndpointAction, LogsEndpointActionResponse, ResponseActionExecuteOutputContent, ResponseActionGetFileOutputContent, ResponseActionGetFileParameters, - ResponseActionParametersWithPidOrEntityId, + ResponseActionParametersWithProcessData, ResponseActionScanOutputContent, ResponseActionsExecuteParameters, ResponseActionScanParameters, @@ -65,6 +65,8 @@ import type { SuspendProcessActionOutputContent, UploadedFileInfo, WithAllKeys, + KillProcessRequestBody, + SuspendProcessRequestBody, } from '../../../../../../common/endpoint/types'; import type { ExecuteActionRequestBody, @@ -79,6 +81,17 @@ import { stringify } from '../../../../utils/stringify'; import { CASE_ATTACHMENT_ENDPOINT_TYPE_ID } from '../../../../../../common/constants'; import { EMPTY_COMMENT } from '../../../../utils/translations'; +const ELASTIC_RESPONSE_ACTION_MESSAGE = ( + username: string = 'system', + command: ResponseActionsApiCommandNames, + responseActionId: string +): string => { + return i18n.translate('xpack.securitySolution.responseActions.comment.message', { + values: { username, command, responseActionId }, + defaultMessage: `Action triggered from Elastic Security by user [{username}] for action [{command} (action id: {responseActionId})]`, + }); +}; + const ENTERPRISE_LICENSE_REQUIRED_MSG = i18n.translate( 'xpack.securitySolution.responseActionsList.error.licenseTooLow', { @@ -176,6 +189,8 @@ export interface ResponseActionsClientPendingAction< export abstract class ResponseActionsClientImpl implements ResponseActionsClient { protected readonly log: Logger; + protected readonly cache = new SimpleMemCache(); + protected abstract readonly agentType: ResponseActionAgentType; constructor(protected readonly options: ResponseActionsClientOptions) { @@ -560,6 +575,26 @@ export abstract class ResponseActionsClientImpl implements ResponseActionsClient usageService.notifyUsage(featureKey); } + /** + * Builds a comment for use in response action requests sent to external EDR systems + * @protected + */ + protected buildExternalComment( + actionRequestIndexOptions: ResponseActionsClientWriteActionRequestToEndpointIndexOptions + ): string { + const { actionId = uuidv4(), comment, command } = actionRequestIndexOptions; + + // If the action request index options does not yet have an actionId assigned to it, then do it now. + // Need to ensure we have an action id for cross-reference. + if (!actionRequestIndexOptions.actionId) { + actionRequestIndexOptions.actionId = actionId; + } + + return ( + ELASTIC_RESPONSE_ACTION_MESSAGE(this.options.username, command, actionId) + + (comment ? `: ${comment}` : '') + ); + } protected async ensureValidActionId(actionId: string): Promise { return validateActionId(this.options.esClient, actionId, this.agentType); } @@ -650,19 +685,19 @@ export abstract class ResponseActionsClientImpl implements ResponseActionsClient } public async killProcess( - actionRequest: KillOrSuspendProcessRequestBody, + actionRequest: KillProcessRequestBody, options?: CommonResponseActionMethodOptions ): Promise< - ActionDetails + ActionDetails > { throw new ResponseActionsNotSupportedError('kill-process'); } public async suspendProcess( - actionRequest: KillOrSuspendProcessRequestBody, + actionRequest: SuspendProcessRequestBody, options?: CommonResponseActionMethodOptions ): Promise< - ActionDetails + ActionDetails > { throw new ResponseActionsNotSupportedError('suspend-process'); } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/simple_mem_cache.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/simple_mem_cache.test.ts new file mode 100644 index 0000000000000..f351e2e40d5be --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/simple_mem_cache.test.ts @@ -0,0 +1,75 @@ +/* + * 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. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { SimpleMemCache } from './simple_mem_cache'; + +describe('SimpleMemCache class', () => { + let cache: SimpleMemCache; + let key: any; + let value: any; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(() => { + cache = new SimpleMemCache(); + key = Symbol('foo'); + value = () => {}; + }); + + it('should `set` and `get` a value to cache', () => { + cache.set(key, value); + + expect(cache.get(key)).toEqual(value); + }); + + it('should accept strings as keys', () => { + key = 'mykey'; + cache.set(key, value); + + expect(cache.get(key)).toEqual(value); + }); + + it('should delete a value from cache', () => { + cache.set(key, value); + + expect(cache.get(key)).toEqual(value); + + cache.delete(key); + + expect(cache.get(key)).toEqual(undefined); + }); + + it('should cleanup expired cache entries', () => { + const key2 = 'myKey'; + cache.set(key, value); // Default ttl of 10s + cache.set(key2, value, 60); // ttl 60s + const dateObj = new Date(); + dateObj.setSeconds(dateObj.getSeconds() + 11); + jest.setSystemTime(dateObj); + cache.cleanup(); + + expect(cache.get(key)).toBeUndefined(); + expect(cache.get(key2)).toEqual(value); + }); + + it('should return undefined when a cache entry exists, but it is expired', () => { + cache.set(key, value); + const dateObj = new Date(); + dateObj.setSeconds(dateObj.getSeconds() + 11); + jest.setSystemTime(dateObj); + + expect(cache.get(key)).toBeUndefined(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/simple_mem_cache.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/simple_mem_cache.ts new file mode 100644 index 0000000000000..fc355bf6c3797 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/simple_mem_cache.ts @@ -0,0 +1,89 @@ +/* + * 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. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export interface SimpleMemCacheInterface { + /** Store a piece of data in cache */ + set( + key: any, + value: any, + /** Time-to-live for this entry only (in seconds) */ + ttl?: number + ): void; + /** Retrieve a piece of data from cache */ + get(key: any): TValue | undefined; + /** Delete a piece of data from cache */ + delete(key: any): void; + /** Clean up the cache by removing all expired entries */ + cleanup(): void; +} + +export interface SimpleMemCacheOptions { + /** + * Default Time-to-live (in seconds) for each piece of data that is cached. + * Defaults to `10` seconds. Can also be set on each entry explicitly + */ + ttl?: number; +} + +interface CachedEntry { + value: any; + expires: number; +} + +/** + * A simple memory caching mechanism. Entries are given a time-to-live (`ttl`) and deleted only when + * attempted to be retrieved and entry is expired. + * + * > **NOTE**: There is no automated "cache cleanup" to remove expired entries over time due to the + * > fact that it could lead to memory leaks. A `cleanup()` method, however, is provided + * > which can be called periodically to clean up the cache + */ +export class SimpleMemCache implements SimpleMemCacheInterface { + private readonly ttl: number; + private readonly cache = new Map(); + + constructor({ ttl = 10 }: SimpleMemCacheOptions = {}) { + this.ttl = ttl; + } + + private isExpired(entry: CachedEntry): boolean { + return entry.expires < Date.now(); + } + + public set(key: any, value: any, ttl = this.ttl): void { + const expiresDt = new Date(); + expiresDt.setSeconds(expiresDt.getSeconds() + ttl); + this.cache.set(key, { value, expires: expiresDt.getTime() }); + } + + public get(key: any): TValue | undefined { + const cachedValue = this.cache.get(key); + + if (cachedValue) { + if (this.isExpired(cachedValue)) { + this.delete(key); + return; + } + + return cachedValue.value as TValue; + } + } + + public delete(key: any): void { + this.cache.delete(key); + } + + public cleanup(): void { + for (const [cacheKey, cacheData] of this.cache.entries()) { + if (this.isExpired(cacheData)) { + this.delete(cacheKey); + } + } + } +} diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts index f95d0b7144a54..4a7b7efd4d4a5 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts @@ -8,9 +8,8 @@ import type { Readable } from 'stream'; import type { ActionDetails, - KillOrSuspendProcessRequestBody, KillProcessActionOutputContent, - ResponseActionParametersWithPidOrEntityId, + ResponseActionParametersWithProcessData, SuspendProcessActionOutputContent, GetProcessesActionOutputContent, ResponseActionGetFileOutputContent, @@ -24,6 +23,8 @@ import type { UploadedFileInfo, ResponseActionScanOutputContent, ResponseActionScanParameters, + KillProcessRequestBody, + SuspendProcessRequestBody, } from '../../../../../../common/endpoint/types'; import type { IsolationRouteRequestBody, @@ -88,17 +89,17 @@ export interface ResponseActionsClient { ) => Promise; killProcess: ( - actionRequest: OmitUnsupportedAttributes, + actionRequest: OmitUnsupportedAttributes, options?: CommonResponseActionMethodOptions ) => Promise< - ActionDetails + ActionDetails >; suspendProcess: ( - actionRequest: OmitUnsupportedAttributes, + actionRequest: OmitUnsupportedAttributes, options?: CommonResponseActionMethodOptions ) => Promise< - ActionDetails + ActionDetails >; runningProcesses: ( diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts index e8b62fb014306..a721bda2f38ab 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts @@ -172,11 +172,10 @@ const createNoParamsResponseActionOptionsMock = ( const createKillOrSuspendProcessOptionsMock = ( overrides: Partial = {} ): KillOrSuspendProcessRequestBody => { + const parameters = overrides.parameters ?? { pid: 999 }; const options: KillOrSuspendProcessRequestBody = { ...createNoParamsResponseActionOptionsMock(), - parameters: { - pid: 999, - }, + parameters, }; return merge(options, overrides); }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/mocks.ts index 0971ed7045655..dac3a5c8f1ddb 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/mocks.ts @@ -8,6 +8,7 @@ import type { SentinelOneGetAgentsResponse, SentinelOneGetActivitiesResponse, + SentinelOneGetRemoteScriptsResponse, } from '@kbn/stack-connectors-plugin/common/sentinelone/types'; import { SENTINELONE_CONNECTOR_ID, @@ -130,6 +131,53 @@ const createSentinelOneAgentDetailsMock = ( ); }; +const createSentinelOneGetRemoteScriptsApiResponseMock = + (): SentinelOneGetRemoteScriptsResponse => { + return { + errors: null, + data: [ + { + bucketName: 'us-east-1-prod-remote-scripts', + createdAt: '2022-07-17T14:02:45.309427Z', + createdByUser: 'SentinelOne', + createdByUserId: '-1', + creator: 'SentinelOne', + creatorId: '-1', + fileName: + '-1/-1/75cYNKCLYJ7kEsjtBSrha0dXTSANJeMmBDQpXlRzPQA%3D/multi-operations-script-bash.sh', + fileSize: 13701, + id: '1466645476786791838', + inputExample: '--terminate --processes ping,chrome --force', + inputInstructions: '--terminate --processes [-f|--force]', + inputRequired: true, + isAvailableForArs: false, + isAvailableForLite: false, + mgmtId: -1, + osTypes: ['macos', 'linux'], + outputFilePaths: null, + package: null, + scopeId: '-1', + scopeLevel: 'sentinel', + scopeName: null, + scopePath: 'Global', + scriptDescription: null, + scriptName: 'Terminate Processes (Linux/macOS)', + scriptRuntimeTimeoutSeconds: 3600, + scriptType: 'action', + shortFileName: 'multi-operations-script-bash.sh', + signature: '75cYNKCLYJ7kEsjtBSrha0dXTSANJeMmBDQpXlRzPQA=', + signatureType: 'SHA-256', + supportedDestinations: null, + updatedAt: '2024-06-30T06:37:53.904005Z', + updater: null, + updaterId: null, + version: '1.0.0', + }, + ], + pagination: { nextCursor: null, totalItems: 1 }, + }; + }; + const createSentinelOneGetActivitiesApiResponseMock = (): SentinelOneGetActivitiesResponse => { return { errors: undefined, @@ -225,6 +273,21 @@ const createConnectorActionsClientMock = (): ActionsClientMock => { data: createSentinelOneGetActivitiesApiResponseMock(), }); + case SUB_ACTION.GET_REMOTE_SCRIPTS: + return responseActionsClientMock.createConnectorActionExecuteResponse({ + data: createSentinelOneGetRemoteScriptsApiResponseMock(), + }); + + case SUB_ACTION.EXECUTE_SCRIPT: + return responseActionsClientMock.createConnectorActionExecuteResponse({ + data: { + data: { + affected: 1, + parentTaskId: 'task-789', + }, + }, + }); + default: return responseActionsClientMock.createConnectorActionExecuteResponse(); } @@ -249,4 +312,5 @@ export const sentinelOneMock = { createConnectorActionsClient: createConnectorActionsClientMock, createConstructorOptions: createConstructorOptionsMock, createSentinelOneActivitiesApiResponse: createSentinelOneGetActivitiesApiResponseMock, + createSentinelOneGetRemoteScriptsApiResponse: createSentinelOneGetRemoteScriptsApiResponseMock, }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts index df326a269f0a3..6a5304f17b82e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts @@ -34,6 +34,7 @@ import type { ResponseActionGetFileOutputContent, ResponseActionGetFileParameters, SentinelOneGetFileRequestMeta, + KillOrSuspendProcessRequestBody, } from '../../../../../../common/endpoint/types'; import type { SearchHit, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import type { ResponseActionGetFileRequestBody } from '../../../../../../common/api/endpoint'; @@ -42,6 +43,7 @@ import { ACTIONS_SEARCH_PAGE_SIZE } from '../../constants'; import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { Readable } from 'stream'; import { RESPONSE_ACTIONS_ZIP_PASSCODE } from '../../../../../../common/endpoint/service/response_actions/constants'; +import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; jest.mock('../../action_details_by_id', () => { const originalMod = jest.requireActual('../../action_details_by_id'); @@ -57,7 +59,7 @@ const getActionDetailsByIdMock = _getActionDetailsById as jest.Mock; describe('SentinelOneActionsClient class', () => { let classConstructorOptions: SentinelOneActionsClientOptionsMock; let s1ActionsClient: ResponseActionsClient; - let connectorActionsMock: NormalizedExternalConnectorClient; + let connectorActionsMock: DeeplyMockedKeys; const createS1IsolationOptions = ( overrides: Omit< @@ -68,11 +70,12 @@ describe('SentinelOneActionsClient class', () => { beforeEach(() => { classConstructorOptions = sentinelOneMock.createConstructorOptions(); - connectorActionsMock = classConstructorOptions.connectorActions; + connectorActionsMock = + classConstructorOptions.connectorActions as DeeplyMockedKeys; s1ActionsClient = new SentinelOneActionsClient(classConstructorOptions); }); - it.each(['killProcess', 'suspendProcess', 'runningProcesses', 'execute', 'upload'] as Array< + it.each(['suspendProcess', 'runningProcesses', 'execute', 'upload', 'scan'] as Array< keyof ResponseActionsClient >)('should throw an un-supported error for %s', async (methodName) => { // @ts-expect-error Purposely passing in empty object for options @@ -794,7 +797,8 @@ describe('SentinelOneActionsClient class', () => { classConstructorOptions.isAutomated = true; classConstructorOptions.connectorActions = responseActionsClientMock.createNormalizedExternalConnectorClient(subActionsClient); - connectorActionsMock = classConstructorOptions.connectorActions; + connectorActionsMock = + classConstructorOptions.connectorActions as DeeplyMockedKeys; // @ts-expect-error readonly prop assignment classConstructorOptions.endpointService.experimentalFeatures.responseActionsSentinelOneGetFileEnabled = true; @@ -1125,4 +1129,169 @@ describe('SentinelOneActionsClient class', () => { }); }); }); + + describe('#killProcess()', () => { + let killProcessActionRequest: KillOrSuspendProcessRequestBody; + + beforeEach(() => { + // @ts-expect-error readonly prop assignment + classConstructorOptions.endpointService.experimentalFeatures.responseActionsSentinelOneKillProcessEnabled = + true; + + killProcessActionRequest = responseActionsClientMock.createKillProcessOptions({ + // @ts-expect-error TS2322 due to type being overloaded to handle kill/suspend process and specific option for S1 + parameters: { process_name: 'foo' }, + }); + }); + + it('should throw an error if feature flag is disabled', async () => { + // @ts-expect-error readonly prop assignment + classConstructorOptions.endpointService.experimentalFeatures.responseActionsSentinelOneKillProcessEnabled = + false; + + await expect(s1ActionsClient.killProcess(killProcessActionRequest)).rejects.toThrow( + `kill-process not supported for sentinel_one agent type. Feature disabled` + ); + }); + + it('should throw an error if `process_name` is not defined (manual mode)', async () => { + // @ts-expect-error + killProcessActionRequest.parameters.process_name = ''; + + await expect(s1ActionsClient.killProcess(killProcessActionRequest)).rejects.toThrow( + '[body.parameters.process_name]: missing parameter or value is empty' + ); + }); + + it('should still create action at error if something goes wrong in automated mode', async () => { + // @ts-expect-error + killProcessActionRequest.parameters.process_name = ''; + classConstructorOptions.isAutomated = true; + classConstructorOptions.connectorActions = + responseActionsClientMock.createNormalizedExternalConnectorClient( + sentinelOneMock.createConnectorActionsClient() + ); + s1ActionsClient = new SentinelOneActionsClient(classConstructorOptions); + await s1ActionsClient.killProcess(killProcessActionRequest); + + expect(classConstructorOptions.esClient.index).toHaveBeenCalledWith( + expect.objectContaining({ + document: expect.objectContaining({ + error: { + message: '[body.parameters.process_name]: missing parameter or value is empty', + }, + }), + }), + { meta: true } + ); + }); + + it('should retrieve script execution information from S1 using host OS', async () => { + await s1ActionsClient.killProcess(killProcessActionRequest); + + expect(connectorActionsMock.execute as jest.Mock).toHaveBeenCalledWith({ + params: { + subAction: SUB_ACTION.GET_REMOTE_SCRIPTS, + subActionParams: { + osTypes: 'linux', + query: 'terminate', + scriptType: 'action', + }, + }, + }); + }); + + it('should throw error if unable to retrieve S1 script information', async () => { + const executeMockImplementation = connectorActionsMock.execute.getMockImplementation()!; + connectorActionsMock.execute.mockImplementation(async (options) => { + if (options.params.subAction === SUB_ACTION.GET_REMOTE_SCRIPTS) { + return responseActionsClientMock.createConnectorActionExecuteResponse({ + data: { data: [] }, + }); + } + return executeMockImplementation.call(connectorActionsMock, options); + }); + + await expect(s1ActionsClient.killProcess(killProcessActionRequest)).rejects.toThrow( + 'Unable to find a script from SentinelOne to handle [kill-process] response action for host running [linux])' + ); + }); + + it('should send execute script request to S1 for kill-process', async () => { + await s1ActionsClient.killProcess(killProcessActionRequest); + + expect(connectorActionsMock.execute).toHaveBeenCalledWith({ + params: { + subAction: 'executeScript', + subActionParams: { + filter: { uuids: '1-2-3' }, + script: { + inputParams: '--terminate --processes "foo" --force', + outputDestination: 'SentinelCloud', + requiresApproval: false, + scriptId: '1466645476786791838', + taskDescription: expect.stringContaining( + 'Action triggered from Elastic Security by user [foo] for action [kill-process' + ), + }, + }, + }, + }); + }); + + it('should return action details on success', async () => { + await s1ActionsClient.killProcess(killProcessActionRequest); + + expect(getActionDetailsByIdMock).toHaveBeenCalled(); + }); + + it('should create action request doc with expected meta info', async () => { + await s1ActionsClient.killProcess(killProcessActionRequest); + + expect(classConstructorOptions.esClient.index).toHaveBeenCalledWith( + { + document: { + '@timestamp': expect.any(String), + EndpointActions: { + action_id: expect.any(String), + data: { + command: 'kill-process', + comment: 'test comment', + parameters: { process_name: 'foo' }, + hosts: { + '1-2-3': { + name: 'sentinelone-1460', + }, + }, + }, + expiration: expect.any(String), + input_type: 'sentinel_one', + type: 'INPUT_ACTION', + }, + agent: { id: ['1-2-3'] }, + user: { id: 'foo' }, + meta: { + agentId: '1845174760470303882', + agentUUID: '1-2-3', + hostName: 'sentinelone-1460', + parentTaskId: 'task-789', + }, + }, + index: ENDPOINT_ACTIONS_INDEX, + refresh: 'wait_for', + }, + { meta: true } + ); + }); + + it('should update cases', async () => { + killProcessActionRequest = { + ...killProcessActionRequest, + case_ids: ['case-1'], + }; + await s1ActionsClient.killProcess(killProcessActionRequest); + + expect(classConstructorOptions.casesClient?.attachments.bulkCreate).toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts index f1f36e993e8a0..54d2147cbe2e2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts @@ -12,11 +12,13 @@ import { import { groupBy } from 'lodash'; import type { ActionTypeExecutorResult } from '@kbn/actions-plugin/common'; import type { + SentinelOneDownloadAgentFileParams, SentinelOneGetActivitiesParams, SentinelOneGetActivitiesResponse, - SentinelOneGetAgentsParams, SentinelOneGetAgentsResponse, - SentinelOneDownloadAgentFileParams, + SentinelOneGetRemoteScriptsParams, + SentinelOneGetRemoteScriptsResponse, + SentinelOneExecuteScriptResponse, } from '@kbn/stack-connectors-plugin/common/sentinelone/types'; import type { QueryDslQueryContainer, @@ -24,11 +26,10 @@ import type { SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; import type { Readable } from 'stream'; +import type { Mutable } from 'utility-types'; +import type { SentinelOneKillProcessScriptArgs, SentinelOneScriptArgs } from './types'; import { ACTIONS_SEARCH_PAGE_SIZE } from '../../constants'; -import type { - NormalizedExternalConnectorClient, - NormalizedExternalConnectorClientExecuteOptions, -} from '../lib/normalized_external_connector_client'; +import type { NormalizedExternalConnectorClient } from '../lib/normalized_external_connector_client'; import { SENTINEL_ONE_ACTIVITY_INDEX_PATTERN } from '../../../../../../common'; import { catchAndWrapError } from '../../../../utils'; import type { @@ -40,16 +41,20 @@ import type { ResponseActionAgentType, ResponseActionsApiCommandNames, } from '../../../../../../common/endpoint/service/response_actions/constants'; +import { RESPONSE_ACTIONS_ZIP_PASSCODE } from '../../../../../../common/endpoint/service/response_actions/constants'; import { stringify } from '../../../../utils/stringify'; import { ResponseActionAgentResponseEsDocNotFound, ResponseActionsClientError } from '../errors'; import type { ActionDetails, EndpointActionDataParameterTypes, EndpointActionResponseDataOutput, + KillProcessActionOutputContent, + KillProcessRequestBody, LogsEndpointAction, LogsEndpointActionResponse, ResponseActionGetFileOutputContent, ResponseActionGetFileParameters, + ResponseActionParametersWithProcessData, SentinelOneActionRequestCommonMeta, SentinelOneActivityDataForType80, SentinelOneActivityEsDoc, @@ -57,7 +62,9 @@ import type { SentinelOneGetFileResponseMeta, SentinelOneIsolationRequestMeta, SentinelOneIsolationResponseMeta, + SentinelOneKillProcessRequestMeta, UploadedFileInfo, + ResponseActionParametersWithProcessName, } from '../../../../../../common/endpoint/types'; import type { IsolationRouteRequestBody, @@ -65,17 +72,29 @@ import type { } from '../../../../../../common/api/endpoint'; import type { ResponseActionsClientOptions, + ResponseActionsClientPendingAction, ResponseActionsClientValidateRequestResponse, ResponseActionsClientWriteActionRequestToEndpointIndexOptions, - ResponseActionsClientPendingAction, } from '../lib/base_response_actions_client'; import { ResponseActionsClientImpl } from '../lib/base_response_actions_client'; -import { RESPONSE_ACTIONS_ZIP_PASSCODE } from '../../../../../../common/endpoint/service/response_actions/constants'; + +const NOOP_THROW = () => { + throw new ResponseActionsClientError('not implemented!'); +}; export type SentinelOneActionsClientOptions = ResponseActionsClientOptions & { connectorActions: NormalizedExternalConnectorClient; }; +interface FetchScriptInfoResponse< + TScriptOptions extends SentinelOneScriptArgs = SentinelOneScriptArgs +> { + scriptId: string; + scriptInfo: SentinelOneGetRemoteScriptsResponse['data'][number]; + /** A helper method that will build the arguments for the given script */ + buildScriptArgs: (options: TScriptOptions) => string; +} + export class SentinelOneActionsClient extends ResponseActionsClientImpl { protected readonly agentType: ResponseActionAgentType = 'sentinel_one'; private readonly connectorActionsClient: NormalizedExternalConnectorClient; @@ -208,28 +227,21 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { private async getAgentDetails( agentUUID: string ): Promise { - const executeOptions: NormalizedExternalConnectorClientExecuteOptions< - SentinelOneGetAgentsParams, - SUB_ACTION - > = { - params: { - subAction: SUB_ACTION.GET_AGENTS, - subActionParams: { - uuid: agentUUID, - }, - }, - }; + const cachedEntry = this.cache.get(agentUUID); + + if (cachedEntry) { + this.log.debug( + `Found cached agent details for UUID [${agentUUID}]:\n${stringify(cachedEntry)}` + ); + return cachedEntry; + } let s1ApiResponse: SentinelOneGetAgentsResponse | undefined; try { - const response = (await this.connectorActionsClient.execute( - executeOptions - )) as ActionTypeExecutorResult; - - this.log.debug( - () => `Response for SentinelOne agent id [${agentUUID}] returned:\n${stringify(response)}` - ); + const response = await this.sendAction(SUB_ACTION.GET_AGENTS, { + uuid: agentUUID, + }); s1ApiResponse = response.data; } catch (err) { @@ -244,6 +256,8 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { throw new ResponseActionsClientError(`SentinelOne agent id [${agentUUID}] not found`, 404); } + this.cache.set(agentUUID, s1ApiResponse.data[0]); + return s1ApiResponse.data[0]; } @@ -261,6 +275,23 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { }; } + // validate that we have a `process_name`. We need this here because the schema for this command + // specifically because `KillProcessRequestBody` allows 3 types of parameters. + if (payload.command === 'kill-process') { + if ( + !payload.parameters || + !('process_name' in payload.parameters) || + !payload.parameters.process_name + ) { + return { + isValid: false, + error: new ResponseActionsClientError( + '[body.parameters.process_name]: missing parameter or value is empty' + ), + }; + } + } + return super.validateRequest(payload); } @@ -439,13 +470,6 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { SentinelOneGetActivitiesResponse<{ commandBatchUuid: string }> >(SUB_ACTION.GET_ACTIVITIES, activitySearchCriteria); - this.log.debug( - () => - `Search of activity log with:\n${stringify( - activitySearchCriteria - )}\n returned:\n${stringify(activityLogSearchResponse.data)}` - ); - if (activityLogSearchResponse.data?.data.length) { const activityLogItem = activityLogSearchResponse.data?.data[0]; @@ -559,6 +583,86 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { }; } + async killProcess( + actionRequest: KillProcessRequestBody, + options?: CommonResponseActionMethodOptions + ): Promise< + ActionDetails + > { + if ( + !this.options.endpointService.experimentalFeatures + .responseActionsSentinelOneKillProcessEnabled + ) { + throw new ResponseActionsClientError( + `kill-process not supported for ${this.agentType} agent type. Feature disabled`, + 400 + ); + } + + const reqIndexOptions: ResponseActionsClientWriteActionRequestToEndpointIndexOptions< + ResponseActionParametersWithProcessName, + KillProcessActionOutputContent, + Partial + > = { + ...actionRequest, + ...this.getMethodOptions(options), + command: 'kill-process', + meta: { parentTaskId: '' }, + }; + + if (!reqIndexOptions.error) { + let error = (await this.validateRequest(reqIndexOptions)).error; + + if (!error) { + const s1AgentDetails = await this.getAgentDetails(reqIndexOptions.endpoint_ids[0]); + const terminateScriptInfo = await this.fetchScriptInfo( + 'kill-process', + s1AgentDetails.osType + ); + + try { + const s1Response = await this.sendAction( + SUB_ACTION.EXECUTE_SCRIPT, + { + filter: { + uuids: actionRequest.endpoint_ids[0], + }, + script: { + scriptId: terminateScriptInfo.scriptId, + taskDescription: this.buildExternalComment(reqIndexOptions), + requiresApproval: false, + outputDestination: 'SentinelCloud', + inputParams: terminateScriptInfo.buildScriptArgs({ + // @ts-expect-error TS2339: Property 'process_name' does not exist (`.validateRequest()` has already validated that `process_name` exists) + processName: reqIndexOptions.parameters.process_name, + }), + }, + } + ); + + reqIndexOptions.meta = { + parentTaskId: s1Response.data?.data?.parentTaskId ?? '', + }; + } catch (err) { + error = err; + } + } + + reqIndexOptions.error = error?.message; + + if (!this.options.isAutomated && error) { + throw error; + } + } + + const { actionDetails } = await this.handleResponseActionCreation< + ResponseActionParametersWithProcessData, + KillProcessActionOutputContent + >(reqIndexOptions); + + return actionDetails; + } + async processPendingActions({ abortSignal, addToQueue, @@ -616,6 +720,93 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { } } + /** + * retrieve script info. for scripts that are used to handle Elastic response actions + * @param scriptType + * @param osType + * @private + */ + private async fetchScriptInfo< + TScriptOptions extends SentinelOneScriptArgs = SentinelOneScriptArgs + >( + scriptType: Extract, + osType: string | 'linux' | 'macos' | 'windows' + ): Promise> { + const searchQueryParams: Mutable> = { + query: '', + osTypes: osType, + }; + let buildScriptArgs = NOOP_THROW as FetchScriptInfoResponse['buildScriptArgs']; + let isDesiredScript: ( + scriptInfo: SentinelOneGetRemoteScriptsResponse['data'][number] + ) => boolean = () => false; + + // Set the query value for filtering the list of scripts in S1 + switch (scriptType) { + case 'kill-process': + searchQueryParams.query = 'terminate'; + searchQueryParams.scriptType = 'action'; + + isDesiredScript = (scriptInfo) => { + return ( + scriptInfo.creator === 'SentinelOne' && + scriptInfo.creatorId === '-1' && + // Using single `-` (instead of double `--`) in match below to ensure both windows and macos/linux are matched + /-terminate/i.test(scriptInfo.inputInstructions ?? '') && + /-processes/i.test(scriptInfo.inputInstructions ?? '') + ); + }; + break; + + default: + throw new ResponseActionsClientError( + `Unable to fetch SentinelOne script for OS [${osType}]. Unknown script type [${scriptType}]` + ); + } + + const { data: scriptSearchResults } = + await this.sendAction( + SUB_ACTION.GET_REMOTE_SCRIPTS, + searchQueryParams + ); + + const s1Script: SentinelOneGetRemoteScriptsResponse['data'][number] | undefined = ( + scriptSearchResults?.data ?? [] + ).find(isDesiredScript); + + if (!s1Script) { + throw new ResponseActionsClientError( + `Unable to find a script from SentinelOne to handle [${scriptType}] response action for host running [${osType}])` + ); + } + + switch (scriptType) { + case 'kill-process': + buildScriptArgs = (args: SentinelOneKillProcessScriptArgs) => { + if (!args.processName) { + throw new ResponseActionsClientError( + `'processName' missing while building script args for [${s1Script.scriptName} (id: ${s1Script.id})] script` + ); + } + + if (osType === 'windows') { + return `-Terminate -Processes "${args.processName}" -Force`; + } + + // Linux + Macos + return `--terminate --processes "${args.processName}" --force`; + }; + + break; + } + + return { + scriptId: s1Script.id, + scriptInfo: s1Script, + buildScriptArgs, + } as FetchScriptInfoResponse; + } + private async fetchGetFileResponseEsDocForAgentId( actionId: string, agentId: string diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/types.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/types.ts index fbb28df5e4449..a51a9ec981765 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/types.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/types.ts @@ -5,22 +5,11 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import type { ActionsClient } from '@kbn/actions-plugin/server'; -import type { SUB_ACTION } from '@kbn/stack-connectors-plugin/common/sentinelone/constants'; - -type ConnectorActionsExecuteOptions = Parameters[0]; - -interface SentinelOneConnectorExecuteParams< - P extends Record = Record -> { - subAction: SUB_ACTION; - subActionParams: P; +export interface SentinelOneKillProcessScriptArgs { + processName: string; } -export type SentinelOneConnectorExecuteOptions< - P extends Record = Record -> = Omit & { - params: SentinelOneConnectorExecuteParams

    & Record; -}; +/** + * All the possible set of arguments running SentinelOne scripts that we support for response actions + */ +export type SentinelOneScriptArgs = SentinelOneKillProcessScriptArgs; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts index f5087b18e03d6..9c9f8e5b62cac 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils/utils.ts @@ -9,7 +9,6 @@ import type { ElasticsearchClient } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { EcsError } from '@elastic/ecs'; import moment from 'moment/moment'; -import { i18n } from '@kbn/i18n'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { FetchActionResponsesResult } from '../..'; import type { @@ -611,12 +610,3 @@ export const createActionDetailsRecord = { return moment().add(2, 'weeks').toISOString(); }; - -export const ELASTIC_RESPONSE_ACTION_MESSAGE = ( - username: string = 'system', - responseActionId: string = 'response-action-id' // I believe actionId exists always and there is a mismatch in types, but this default is just a safety net -): string => - i18n.translate('xpack.securitySolution.responseActions.comment.message', { - values: { username, responseActionId }, - defaultMessage: `Action triggered from Elastic Security by user {username} for action {responseActionId}`, - }); diff --git a/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.test.ts b/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.test.ts index 4c9fe188fdffb..dfd38ab8e6eea 100644 --- a/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.test.ts +++ b/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.test.ts @@ -5,12 +5,10 @@ * 2.0. */ import type { Logger, SavedObjectsFindResponse } from '@kbn/core/server'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { INTERNAL_DASHBOARDS_URL } from '../../../../common/constants'; import { serverMock, requestContextMock, - mockGetCurrentUser, requestMock, } from '../../detection_engine/routes/__mocks__'; import { mockGetDashboardsResult } from '../__mocks__'; @@ -18,7 +16,6 @@ import { getDashboardsByTagsRoute } from './get_dashboards_by_tags'; describe('getDashboardsByTagsRoute', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; const { context } = requestContextMock.createTools(); const logger = { error: jest.fn() } as unknown as Logger; @@ -36,14 +33,7 @@ describe('getDashboardsByTagsRoute', () => { jest.clearAllMocks(); server = serverMock.create(); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - - getDashboardsByTagsRoute(server.router, logger, securitySetup); + getDashboardsByTagsRoute(server.router, logger); }); it('should return dashboards with Security Solution tags', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts b/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts index ef4695aea6ea5..e7a3ebf9a7f10 100644 --- a/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts +++ b/x-pack/plugins/security_solution/server/lib/dashboards/routes/get_dashboards_by_tags.ts @@ -10,18 +10,13 @@ import { i18n } from '@kbn/i18n'; import type { DashboardAttributes } from '@kbn/dashboard-plugin/common'; import { transformError } from '@kbn/securitysolution-es-utils'; import { INTERNAL_DASHBOARDS_URL } from '../../../../common/constants'; -import type { SetupPlugins } from '../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { buildSiemResponse } from '../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../timeline/utils/common'; import { getDashboardsRequest } from '../../../../common/api/tags'; import { buildRouteValidationWithExcess } from '../../../utils/build_validation/route_validation'; -export const getDashboardsByTagsRoute = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - security: SetupPlugins['security'] -) => { +export const getDashboardsByTagsRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .post({ path: INTERNAL_DASHBOARDS_URL, @@ -36,7 +31,7 @@ export const getDashboardsByTagsRoute = ( validate: { request: { body: buildRouteValidationWithExcess(getDashboardsRequest) } }, }, async (context, request, response) => { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const savedObjectsClient = (await frameworkRequest.context.core).savedObjects.client; const { tagIds } = request.body; diff --git a/x-pack/plugins/security_solution/server/lib/dashboards/routes/index.ts b/x-pack/plugins/security_solution/server/lib/dashboards/routes/index.ts index 17b6cec643ec4..dfbb6fbb8771d 100644 --- a/x-pack/plugins/security_solution/server/lib/dashboards/routes/index.ts +++ b/x-pack/plugins/security_solution/server/lib/dashboards/routes/index.ts @@ -5,14 +5,9 @@ * 2.0. */ import type { Logger } from '@kbn/core/server'; -import type { SetupPlugins } from '../../../plugin_contract'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { getDashboardsByTagsRoute } from './get_dashboards_by_tags'; -export const registerDashboardsRoutes = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - security: SetupPlugins['security'] -) => { - getDashboardsByTagsRoute(router, logger, security); +export const registerDashboardsRoutes = (router: SecuritySolutionPluginRouter, logger: Logger) => { + getDashboardsByTagsRoute(router, logger); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.test.ts index 9e9bd77e81a1c..8e029f939d111 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { securityServiceMock } from '@kbn/core/server/mocks'; import { getPrebuiltRulesAndTimelinesStatusRoute } from './get_prebuilt_rules_and_timelines_status_route'; import { @@ -13,7 +14,6 @@ import { getPrepackagedRulesStatusRequest, } from '../../../routes/__mocks__/request_responses'; import { requestContextMock, serverMock } from '../../../routes/__mocks__'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { checkTimelinesStatus } from '../../../../timeline/utils/check_timelines_status'; import { mockCheckTimelinesStatusBeforeInstallResult, @@ -56,6 +56,7 @@ jest.mock('../../../../timeline/utils/check_timelines_status', () => { }); describe('get_prepackaged_rule_status_route', () => { + const securityCore = securityServiceMock.createStart(); const mockGetCurrentUser = { user: { username: 'mockUser', @@ -64,19 +65,13 @@ describe('get_prepackaged_rule_status_route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let securitySetup: SecurityPluginSetup; beforeEach(() => { jest.clearAllMocks(); server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; + jest.spyOn(securityCore.authc, 'getCurrentUser').mockReturnValue(mockGetCurrentUser); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); @@ -86,7 +81,7 @@ describe('get_prepackaged_rule_status_route', () => { prepackagedTimelines: [], }); - getPrebuiltRulesAndTimelinesStatusRoute(server.router, securitySetup); + getPrebuiltRulesAndTimelinesStatusRoute(server.router); }); describe('status codes', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.ts index 4848cb5e52c4e..84cb18032f50e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route.ts @@ -9,7 +9,6 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { validate } from '@kbn/securitysolution-io-ts-utils'; import { checkTimelineStatusRt } from '../../../../../../common/api/timeline'; import { buildSiemResponse } from '../../../routes/utils'; -import type { SetupPlugins } from '../../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { @@ -27,10 +26,7 @@ import { rulesToMap } from '../../logic/utils'; import { buildFrameworkRequest } from '../../../../timeline/utils/common'; import { checkTimelinesStatus } from '../../../../timeline/utils/check_timelines_status'; -export const getPrebuiltRulesAndTimelinesStatusRoute = ( - router: SecuritySolutionPluginRouter, - security: SetupPlugins['security'] -) => { +export const getPrebuiltRulesAndTimelinesStatusRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .get({ access: 'public', @@ -71,7 +67,7 @@ export const getPrebuiltRulesAndTimelinesStatusRoute = ( const rulesToInstall = getRulesToInstall(latestPrebuiltRules, installedPrebuiltRules); const rulesToUpdate = getRulesToUpdate(latestPrebuiltRules, installedPrebuiltRules); - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const prebuiltTimelineStatus = await checkTimelinesStatus(frameworkRequest); const [validatedPrebuiltTimelineStatus] = validate( prebuiltTimelineStatus, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts index 1ac2ef2a3954d..71740086e3fa5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { SetupPlugins } from '../../../../plugin_contract'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { getPrebuiltRulesAndTimelinesStatusRoute } from './get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route'; @@ -16,12 +15,9 @@ import { reviewRuleUpgradeRoute } from './review_rule_upgrade/review_rule_upgrad import { performRuleInstallationRoute } from './perform_rule_installation/perform_rule_installation_route'; import { performRuleUpgradeRoute } from './perform_rule_upgrade/perform_rule_upgrade_route'; -export const registerPrebuiltRulesRoutes = ( - router: SecuritySolutionPluginRouter, - security: SetupPlugins['security'] -) => { +export const registerPrebuiltRulesRoutes = (router: SecuritySolutionPluginRouter) => { // Legacy endpoints that we're going to deprecate - getPrebuiltRulesAndTimelinesStatusRoute(router, security); + getPrebuiltRulesAndTimelinesStatusRoute(router); installPrebuiltRulesAndTimelinesRoute(router); // New endpoints for the rule upgrade and installation workflows diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/review_rule_installation_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/review_rule_installation_route.ts index 0b30c9bab4782..ec3ca342bf8c9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/review_rule_installation_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_installation/review_rule_installation_route.ts @@ -18,7 +18,7 @@ import { createPrebuiltRuleObjectsClient } from '../../logic/rule_objects/prebui import { fetchRuleVersionsTriad } from '../../logic/rule_versions/fetch_rule_versions_triad'; import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; import { getVersionBuckets } from '../../model/rule_versions/get_version_buckets'; -import { convertPrebuiltRuleAssetToRuleResponse } from '../../../rule_management/normalization/rule_converters'; +import { convertPrebuiltRuleAssetToRuleResponse } from '../../../rule_management/logic/detection_rules_client/converters/convert_prebuilt_rule_asset_to_rule_response'; import { PREBUILT_RULES_OPERATION_SOCKET_TIMEOUT_MS } from '../../constants'; export const reviewRuleInstallationRoute = (router: SecuritySolutionPluginRouter) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts index de7db929790de..f38fcc7953641 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts @@ -27,7 +27,7 @@ import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt import { createPrebuiltRuleObjectsClient } from '../../logic/rule_objects/prebuilt_rule_objects_client'; import { fetchRuleVersionsTriad } from '../../logic/rule_versions/fetch_rule_versions_triad'; import { getVersionBuckets } from '../../model/rule_versions/get_version_buckets'; -import { convertPrebuiltRuleAssetToRuleResponse } from '../../../rule_management/normalization/rule_converters'; +import { convertPrebuiltRuleAssetToRuleResponse } from '../../../rule_management/logic/detection_rules_client/converters/convert_prebuilt_rule_asset_to_rule_response'; import { PREBUILT_RULES_OPERATION_SOCKET_TIMEOUT_MS } from '../../constants'; export const reviewRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts index ea482858650fb..5e9bd8bd9ae64 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts @@ -173,7 +173,6 @@ const calculateCommonFieldsDiff = ( const commonFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { rule_id: simpleDiffAlgorithm, version: numberDiffAlgorithm, - meta: simpleDiffAlgorithm, name: singleLineStringDiffAlgorithm, tags: simpleDiffAlgorithm, description: simpleDiffAlgorithm, @@ -191,8 +190,6 @@ const commonFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor author: simpleDiffAlgorithm, license: singleLineStringDiffAlgorithm, rule_schedule: simpleDiffAlgorithm, - actions: simpleDiffAlgorithm, - throttle: simpleDiffAlgorithm, exceptions_list: simpleDiffAlgorithm, max_signals: numberDiffAlgorithm, rule_name_override: simpleDiffAlgorithm, @@ -211,7 +208,6 @@ const customQueryFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor { +): RequiredOptional => { return { // --------------------- REQUIRED FIELDS // Technical fields rule_id: rule.rule_id, version: rule.version, - meta: rule.meta ?? {}, // Main domain fields name: rule.name, @@ -140,8 +139,6 @@ const extractDiffableCommonFields = ( // Other domain fields rule_schedule: extractRuleSchedule(rule), - actions: (rule.actions ?? []) as RuleActionArray, - throttle: rule.throttle ?? 'no_actions', exceptions_list: rule.exceptions_list ?? [], max_signals: rule.max_signals ?? DEFAULT_MAX_SIGNALS, @@ -155,29 +152,27 @@ const extractDiffableCommonFields = ( const extractDiffableCustomQueryFields = ( rule: QueryRule | QueryRuleCreateProps -): DiffableCustomQueryFields => { +): RequiredOptional => { return { type: rule.type, kql_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), data_source: extractRuleDataSource(rule.index, rule.data_view_id), - alert_suppression: rule.alert_suppression, }; }; const extractDiffableSavedQueryFieldsFromRuleObject = ( rule: SavedQueryRule | SavedQueryRuleCreateProps -): DiffableSavedQueryFields => { +): RequiredOptional => { return { type: rule.type, kql_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), data_source: extractRuleDataSource(rule.index, rule.data_view_id), - alert_suppression: rule.alert_suppression, }; }; const extractDiffableEqlFieldsFromRuleObject = ( rule: EqlRule | EqlRuleCreateProps -): DiffableEqlFields => { +): RequiredOptional => { return { type: rule.type, eql_query: extractRuleEqlQuery(rule.query, rule.language, rule.filters), @@ -190,7 +185,7 @@ const extractDiffableEqlFieldsFromRuleObject = ( const extractDiffableEsqlFieldsFromRuleObject = ( rule: EsqlRule | EsqlRuleCreateProps -): DiffableEsqlFields => { +): RequiredOptional => { return { type: rule.type, esql_query: extractRuleEsqlQuery(rule.query, rule.language), @@ -199,7 +194,7 @@ const extractDiffableEsqlFieldsFromRuleObject = ( const extractDiffableThreatMatchFieldsFromRuleObject = ( rule: ThreatMatchRule | ThreatMatchRuleCreateProps -): DiffableThreatMatchFields => { +): RequiredOptional => { return { type: rule.type, kql_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), @@ -219,7 +214,7 @@ const extractDiffableThreatMatchFieldsFromRuleObject = ( const extractDiffableThresholdFieldsFromRuleObject = ( rule: ThresholdRule | ThresholdRuleCreateProps -): DiffableThresholdFields => { +): RequiredOptional => { return { type: rule.type, kql_query: extractRuleKqlQuery(rule.query, rule.language, rule.filters, rule.saved_id), @@ -230,7 +225,7 @@ const extractDiffableThresholdFieldsFromRuleObject = ( const extractDiffableMachineLearningFieldsFromRuleObject = ( rule: MachineLearningRule | MachineLearningRuleCreateProps -): DiffableMachineLearningFields => { +): RequiredOptional => { return { type: rule.type, machine_learning_job_id: rule.machine_learning_job_id, @@ -240,7 +235,7 @@ const extractDiffableMachineLearningFieldsFromRuleObject = ( const extractDiffableNewTermsFieldsFromRuleObject = ( rule: NewTermsRule | NewTermsRuleCreateProps -): DiffableNewTermsFields => { +): RequiredOptional => { return { type: rule.type, kql_query: extractInlineKqlQuery(rule.query, rule.language, rule.filters), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_schedule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_schedule.ts index 28880523c774c..a15a4fcb930cd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_schedule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/extract_rule_schedule.ts @@ -9,7 +9,10 @@ import moment from 'moment'; import dateMath from '@elastic/datemath'; import { parseDuration } from '@kbn/alerting-plugin/common'; -import type { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import type { + RuleMetadata, + RuleResponse, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; import type { RuleSchedule } from '../../../../../../../common/api/detection_engine/prebuilt_rules'; import type { PrebuiltRuleAsset } from '../../../model/rule_assets/prebuilt_rule_asset'; @@ -18,8 +21,7 @@ export const extractRuleSchedule = (rule: RuleResponse | PrebuiltRuleAsset): Rul const from = rule.from ?? 'now-6m'; const to = rule.to ?? 'now'; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const ruleMeta = (rule.meta ?? {}) as any; + const ruleMeta: RuleMetadata = ('meta' in rule ? rule.meta : undefined) ?? {}; const lookbackFromMeta = String(ruleMeta.from ?? ''); const intervalDuration = parseInterval(interval); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client.ts new file mode 100644 index 0000000000000..0776fefb98656 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client.ts @@ -0,0 +1,14 @@ +/* + * 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 const createPrebuiltRuleAssetsClient = () => { + return { + fetchLatestAssets: jest.fn(), + fetchLatestVersions: jest.fn(), + fetchAssetsByVersion: jest.fn(), + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client.ts index 7ad4df3cddabd..1138a48cc39d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client.ts @@ -13,7 +13,7 @@ import type { import { withSecuritySpan } from '../../../../../utils/with_security_span'; import { findRules } from '../../../rule_management/logic/search/find_rules'; import { getExistingPrepackagedRules } from '../../../rule_management/logic/search/get_existing_prepackaged_rules'; -import { internalRuleToAPIResponse } from '../../../rule_management/normalization/rule_converters'; +import { internalRuleToAPIResponse } from '../../../rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response'; export interface IPrebuiltRuleObjectsClient { fetchAllInstalledRules(): Promise; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts index 06ccf0a2b97f4..e4d9255dc2b95 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts @@ -32,6 +32,33 @@ describe('Prebuilt rule asset schema', () => { expect(result.data).toEqual(getPrebuiltRuleMock()); }); + describe('ommited fields from the rule schema are ignored', () => { + // The PrebuiltRuleAsset schema is built out of the rule schema, + // but the following fields are manually omitted. + // See: detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts + const omittedFields = [ + 'actions', + 'throttle', + 'meta', + 'output_index', + 'namespace', + 'alias_purpose', + 'alias_target_id', + 'outcome', + ]; + + test.each(omittedFields)('ignores %s since it`s an omitted field', (field) => { + const payload: Partial & Record = { + ...getPrebuiltRuleMock(), + [field]: 'some value', + }; + + const result = PrebuiltRuleAsset.safeParse(payload); + expectParseSuccess(result); + expect(result.data).toEqual(getPrebuiltRuleMock()); + }); + }); + test('[rule_id] does not validate', () => { const payload: Partial = { rule_id: 'rule-1', @@ -64,17 +91,6 @@ describe('Prebuilt rule asset schema', () => { expect(result.data).toEqual(payload); }); - test('You can send in a namespace', () => { - const payload: PrebuiltRuleAsset = { - ...getPrebuiltRuleMock(), - namespace: 'a namespace', - }; - - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseSuccess(result); - expect(result.data).toEqual(payload); - }); - test('You can send in an empty array to threat', () => { const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), @@ -449,32 +465,6 @@ describe('Prebuilt rule asset schema', () => { expect(result.data).toEqual(payload); }); - test('You can set meta to any object you want', () => { - const payload: PrebuiltRuleAsset = { - ...getPrebuiltRuleMock(), - meta: { - somethingMadeUp: { somethingElse: true }, - }, - }; - - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseSuccess(result); - expect(result.data).toEqual(payload); - }); - - test('You cannot create meta as a string', () => { - const payload: Omit & { meta: string } = { - ...getPrebuiltRuleMock(), - meta: 'should not work', - }; - - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"meta: Expected object, received string"` - ); - }); - test('validates with timeline_id and timeline_title', () => { const payload: PrebuiltRuleAsset = { ...getPrebuiltRuleMock(), @@ -500,71 +490,6 @@ describe('Prebuilt rule asset schema', () => { ); }); - test('You cannot send in an array of actions that are missing "group"', () => { - const payload: Omit = { - ...getPrebuiltRuleMock(), - actions: [{ id: 'id', action_type_id: 'action_type_id', params: {} }], - }; - - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"actions.0.group: Required"`); - }); - - test('You cannot send in an array of actions that are missing "id"', () => { - const payload: Omit = { - ...getPrebuiltRuleMock(), - actions: [{ group: 'group', action_type_id: 'action_type_id', params: {} }], - }; - - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"actions.0.id: Required"`); - }); - - test('You cannot send in an array of actions that are missing "action_type_id"', () => { - const payload: Omit = { - ...getPrebuiltRuleMock(), - actions: [{ group: 'group', id: 'id', params: {} }], - }; - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.action_type_id: Required"` - ); - }); - - test('You cannot send in an array of actions that are missing "params"', () => { - const payload: Omit = { - ...getPrebuiltRuleMock(), - actions: [{ group: 'group', id: 'id', action_type_id: 'action_type_id' }], - }; - - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"actions.0.params: Required"`); - }); - - test('You cannot send in an array of actions that are including "actionTypeId"', () => { - const payload: Omit = { - ...getPrebuiltRuleMock(), - actions: [ - { - group: 'group', - id: 'id', - actionTypeId: 'actionTypeId', - params: {}, - }, - ], - }; - - const result = PrebuiltRuleAsset.safeParse(payload); - expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.action_type_id: Required"` - ); - }); - describe('note', () => { test('You can set note to a string', () => { const payload: PrebuiltRuleAsset = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts index 38ead608a3b75..056a3998e3b3e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts @@ -10,9 +10,64 @@ import { RuleSignatureId, RuleVersion, BaseCreateProps, - TypeSpecificCreateProps, + EqlRuleCreateFields, + EsqlRuleCreateFields, + MachineLearningRuleCreateFields, + NewTermsRuleCreateFields, + QueryRuleCreateFields, + SavedQueryRuleCreateFields, + ThreatMatchRuleCreateFields, + ThresholdRuleCreateFields, } from '../../../../../../common/api/detection_engine/model/rule_schema'; +/** + * The PrebuiltRuleAsset schema is created based on the rule schema defined in our OpenAPI specs. + * However, we don't need all the rule schema fields to be present in the PrebuiltRuleAsset. + * We omit some of them because they are not present in https://github.com/elastic/detection-rules. + * Context: https://github.com/elastic/kibana/issues/180393 + */ +const BASE_PROPS_REMOVED_FROM_PREBUILT_RULE_ASSET = zodMaskFor()([ + 'actions', + 'throttle', + 'meta', + 'output_index', + 'namespace', + 'alias_purpose', + 'alias_target_id', + 'outcome', +]); + +// `response_actions` is only part of the optional fields in QueryRuleCreateFields and SavedQueryRuleCreateFields +const TYPE_SPECIFIC_PROPS_REMOVED_FROM_PREBUILT_RULE_ASSET = zodMaskFor< + QueryRuleCreateFields | SavedQueryRuleCreateFields +>()(['response_actions']); + +const QueryRuleAssetFields = QueryRuleCreateFields.omit( + TYPE_SPECIFIC_PROPS_REMOVED_FROM_PREBUILT_RULE_ASSET +); +const SavedQueryRuleAssetFields = SavedQueryRuleCreateFields.omit( + TYPE_SPECIFIC_PROPS_REMOVED_FROM_PREBUILT_RULE_ASSET +); + +export const RuleAssetTypeSpecificCreateProps = z.discriminatedUnion('type', [ + EqlRuleCreateFields, + QueryRuleAssetFields, + SavedQueryRuleAssetFields, + ThresholdRuleCreateFields, + ThreatMatchRuleCreateFields, + MachineLearningRuleCreateFields, + NewTermsRuleCreateFields, + EsqlRuleCreateFields, +]); + +function zodMaskFor() { + return function (props: U[]): Record { + type PropObject = Record; + const propObjects: PropObject[] = props.map((p: U) => ({ [p]: true })); + return Object.assign({}, ...propObjects); + }; +} + /** * Asset containing source content of a prebuilt Security detection rule. * Is defined for each prebuilt rule in https://github.com/elastic/detection-rules. @@ -24,13 +79,16 @@ import { * - Data Exfiltration Detection * * Big differences between this schema and RuleCreateProps: - * - rule_id is required here - * - version is a required field that must exist + * - rule_id is a required field + * - version is a required field + * - some fields are omitted because they are not present in https://github.com/elastic/detection-rules */ export type PrebuiltRuleAsset = z.infer; -export const PrebuiltRuleAsset = BaseCreateProps.and(TypeSpecificCreateProps).and( - z.object({ - rule_id: RuleSignatureId, - version: RuleVersion, - }) -); +export const PrebuiltRuleAsset = BaseCreateProps.omit(BASE_PROPS_REMOVED_FROM_PREBUILT_RULE_ASSET) + .and(RuleAssetTypeSpecificCreateProps) + .and( + z.object({ + rule_id: RuleSignatureId, + version: RuleVersion, + }) + ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts index 5d81134325c50..094f3e560ec3e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts @@ -6,7 +6,6 @@ */ import { requestMock, serverMock } from '../__mocks__'; -import type { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; import { getCreateSignalsMigrationSchemaMock } from '../../../../../common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.mock'; import { getIndexVersionsByIndex } from '../../migrations/get_index_versions_by_index'; @@ -43,13 +42,7 @@ describe('creating signals migrations route', () => { (getIndexVersionsByIndex as jest.Mock).mockResolvedValue({ 'my-signals-index': -1 }); (getSignalVersionsByIndex as jest.Mock).mockResolvedValue({ 'my-signals-index': [] }); - const securityMock = { - authc: { - getCurrentUser: jest.fn().mockReturnValue({ user: { username: 'my-username' } }), - }, - } as unknown as SetupPlugins['security']; - - createSignalsMigrationRoute(server.router, securityMock); + createSignalsMigrationRoute(server.router); }); it('passes options to the createMigration', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts index b6e7ae696b755..8d8d80a700478 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts @@ -9,7 +9,6 @@ import { transformError, BadRequestError, getIndexAliases } from '@kbn/securitys import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { CreateAlertsMigrationRequestBody } from '../../../../../common/api/detection_engine/signals_migration'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import type { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; import { buildSiemResponse } from '../utils'; @@ -20,10 +19,7 @@ import { isOutdated, signalsAreOutdated } from '../../migrations/helpers'; import { getIndexVersionsByIndex } from '../../migrations/get_index_versions_by_index'; import { getSignalVersionsByIndex } from '../../migrations/get_signal_versions_by_index'; -export const createSignalsMigrationRoute = ( - router: SecuritySolutionPluginRouter, - security: SetupPlugins['security'] -) => { +export const createSignalsMigrationRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .post({ path: DETECTION_ENGINE_SIGNALS_MIGRATION_URL, @@ -53,7 +49,7 @@ export const createSignalsMigrationRoute = ( if (!appClient) { return siemResponse.error({ statusCode: 404 }); } - const user = await security?.authc.getCurrentUser(request); + const user = core.security.authc.getCurrentUser(); const migrationService = signalsMigrationService({ esClient, soClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts index f4452c73eaf78..c4838280ac6a4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts @@ -9,17 +9,13 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { AlertsMigrationCleanupRequestBody } from '../../../../../common/api/detection_engine/signals_migration'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import type { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; import { buildSiemResponse } from '../utils'; import { signalsMigrationService } from '../../migrations/migration_service'; import { getMigrationSavedObjectsById } from '../../migrations/get_migration_saved_objects_by_id'; -export const deleteSignalsMigrationRoute = ( - router: SecuritySolutionPluginRouter, - security: SetupPlugins['security'] -) => { +export const deleteSignalsMigrationRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .delete({ path: DETECTION_ENGINE_SIGNALS_MIGRATION_URL, @@ -50,7 +46,7 @@ export const deleteSignalsMigrationRoute = ( return siemResponse.error({ statusCode: 404 }); } - const user = await security?.authc.getCurrentUser(request); + const user = core.security.authc.getCurrentUser(); const migrationService = signalsMigrationService({ esClient, soClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.test.ts index 341cff93ccc63..8183c0bbac7bd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.test.ts @@ -6,7 +6,6 @@ */ import { serverMock } from '../__mocks__'; -import type { SetupPlugins } from '../../../../plugin'; import { getFinalizeSignalsMigrationRequest } from '../__mocks__/request_responses'; import { getMigrationSavedObjectsById } from '../../migrations/get_migration_saved_objects_by_id'; import { getSignalsMigrationSavedObjectMock } from '../../migrations/saved_objects_schema.mock'; @@ -22,14 +21,9 @@ describe('finalizing signals migrations', () => { beforeEach(() => { server = serverMock.create(); - const securityMock = { - authc: { - getCurrentUser: jest.fn().mockReturnValue({ user: { username: 'my-username' } }), - }, - } as unknown as SetupPlugins['security']; const ruleDataPluginServiceMock = ruleDataServiceMock.create() as unknown as RuleDataPluginService; - finalizeSignalsMigrationRoute(server.router, ruleDataPluginServiceMock, securityMock); + finalizeSignalsMigrationRoute(server.router, ruleDataPluginServiceMock); }); it('returns an empty array error if no migrations exists', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts index 468baae7fdcad..9e09ffe0cf895 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts @@ -10,7 +10,6 @@ import type { RuleDataPluginService } from '@kbn/rule-registry-plugin/server'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { FinalizeAlertsMigrationRequestBody } from '../../../../../common/api/detection_engine/signals_migration'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import type { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL } from '../../../../../common/constants'; import { isMigrationFailed, isMigrationPending } from '../../migrations/helpers'; import { signalsMigrationService } from '../../migrations/migration_service'; @@ -20,8 +19,7 @@ import { getMigrationSavedObjectsById } from '../../migrations/get_migration_sav export const finalizeSignalsMigrationRoute = ( router: SecuritySolutionPluginRouter, - ruleDataService: RuleDataPluginService, - security: SetupPlugins['security'] + ruleDataService: RuleDataPluginService ) => { router.versioned .post({ @@ -53,7 +51,7 @@ export const finalizeSignalsMigrationRoute = ( if (!appClient) { return siemResponse.error({ statusCode: 404 }); } - const user = await security?.authc.getCurrentUser(request); + const user = core.security.authc.getCurrentUser(); const migrationService = signalsMigrationService({ esClient, soClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index 1b2bdb6ca1ef4..a1569e8bcab07 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../../common/constants'; import { @@ -24,18 +24,8 @@ describe('set signal status', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); let logger: ReturnType; - let mockCore: ReturnType; beforeEach(() => { - mockCore = coreMock.createSetup({ - pluginStartDeps: { - security: { - authc: { - getCurrentUser: jest.fn().mockReturnValue({ user: { username: 'my-username' } }), - }, - }, - }, - }); server = serverMock.create(); logger = loggingSystemMock.createLogger(); ({ context } = requestContextMock.createTools()); @@ -44,7 +34,7 @@ describe('set signal status', () => { getSuccessfulSignalUpdateResponse() ); const telemetrySenderMock = createMockTelemetryEventsSender(); - setSignalsStatusRoute(server.router, logger, telemetrySenderMock, mockCore.getStartServices); + setSignalsStatusRoute(server.router, logger, telemetrySenderMock); }); describe('status on signal', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index a92f3a54ebac7..dde24af7007c4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -12,8 +12,7 @@ import { ALERT_WORKFLOW_STATUS_UPDATED_AT, ALERT_WORKFLOW_USER, } from '@kbn/rule-data-utils'; -import type { ElasticsearchClient, Logger, StartServicesAccessor } from '@kbn/core/server'; -import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import type { AuthenticatedUser, ElasticsearchClient, Logger } from '@kbn/core/server'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { SetAlertsStatusRequestBody } from '../../../../../common/api/detection_engine/signals'; import type { SecuritySolutionPluginRouter } from '../../../../types'; @@ -24,7 +23,6 @@ import { import { buildSiemResponse } from '../utils'; import type { ITelemetryEventsSender } from '../../../telemetry/sender'; import { INSIGHTS_CHANNEL } from '../../../telemetry/constants'; -import type { StartPlugins } from '../../../../plugin'; import { getSessionIDfromKibanaRequest, createAlertStatusPayloads, @@ -33,8 +31,7 @@ import { export const setSignalsStatusRoute = ( router: SecuritySolutionPluginRouter, logger: Logger, - sender: ITelemetryEventsSender, - startServices: StartServicesAccessor + sender: ITelemetryEventsSender ) => { router.versioned .post({ @@ -65,14 +62,10 @@ export const setSignalsStatusRoute = ( if (!siemClient) { return siemResponse.error({ statusCode: 404 }); } - const [_, { security }] = await startServices(); - const user = security.authc.getCurrentUser(request); + const user = core.security.authc.getCurrentUser(); const clusterId = sender.getClusterID(); - const [isTelemetryOptedIn, username] = await Promise.all([ - sender.isTelemetryOptedIn(), - security?.authc.getCurrentUser(request)?.username, - ]); + const isTelemetryOptedIn = await sender.isTelemetryOptedIn(); if (isTelemetryOptedIn && clusterId) { // Sometimes the ids are in the query not passed in the request? @@ -82,12 +75,12 @@ export const setSignalsStatusRoute = ( : (get(request.body.query, 'bool.filter.terms._id') as string[]); // Get Context for Insights Payloads const sessionId = getSessionIDfromKibanaRequest(clusterId, request); - if (username && toSendAlertIds && sessionId && status) { + if (user?.username && toSendAlertIds && sessionId && status) { const insightsPayloads = createAlertStatusPayloads( clusterId, toSendAlertIds, sessionId, - username, + user.username, DETECTION_ENGINE_SIGNALS_STATUS_URL, status ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts index c8c257770e26a..060043c14819f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts @@ -295,7 +295,7 @@ export const createAndAssociateDefaultExceptionList = async ({ : existingRuleExceptionLists; await detectionRulesClient.patchRule({ - nextParams: { + rulePatch: { rule_id: rule.params.ruleId, ...rule.params, exceptions_list: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_actions_response.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_actions_response.ts index 2bf14ccbf085e..1b78da705e346 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_actions_response.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_actions_response.ts @@ -25,7 +25,7 @@ import type { BulkActionsDryRunErrCode } from '../../../../../../../common/const import type { PromisePoolError } from '../../../../../../utils/promise_pool'; import type { RuleAlertType } from '../../../../rule_schema'; import type { DryRunError } from '../../../logic/bulk_actions/dry_run'; -import { internalRuleToAPIResponse } from '../../../normalization/rule_converters'; +import { internalRuleToAPIResponse } from '../../../logic/detection_rules_client/converters/internal_rule_to_api_response'; const MAX_ERROR_MESSAGE_LENGTH = 1000; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts index 3b16ba5fa4742..da75e4e33362a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts @@ -86,7 +86,7 @@ export const bulkPatchRulesRoute = (router: SecuritySolutionPluginRouter, logger }); const patchedRule = await detectionRulesClient.patchRule({ - nextParams: payloadRule, + rulePatch: payloadRule, }); return patchedRule; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts index 0e508f43103d6..3886f63c482b0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts @@ -76,7 +76,7 @@ export const patchRuleRoute = (router: SecuritySolutionPluginRouter) => { }); const patchedRule = await detectionRulesClient.patchRule({ - nextParams: params, + rulePatch: params, }); return response.ok({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/index.ts index 7e379651b2faf..f2e147ef3154f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/index.ts @@ -7,12 +7,7 @@ export * from './api/register_routes'; -// TODO: https://github.com/elastic/kibana/pull/142950 -// TODO: Revisit and consider moving to the rule_schema subdomain -export { - commonParamsCamelToSnake, - typeSpecificCamelToSnake, - convertCreateAPIToInternalSchema, -} from './normalization/rule_converters'; +export { commonParamsCamelToSnake } from './logic/detection_rules_client/converters/common_params_camel_to_snake'; +export { typeSpecificCamelToSnake } from './logic/detection_rules_client/converters/type_specific_camel_to_snake'; export { transformFromAlertThrottle, transformToNotifyWhen } from './normalization/rule_actions'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts index dd22dac3adc77..1dfa3a9b0e9ad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts @@ -12,7 +12,6 @@ import type { SanitizedRule } from '@kbn/alerting-plugin/common'; import { SERVER_APP_ID } from '../../../../../../common/constants'; import type { InternalRuleCreate, RuleParams } from '../../../rule_schema'; import { transformToActionFrequency } from '../../normalization/rule_actions'; -import { convertImmutableToRuleSource } from '../../normalization/rule_converters'; const DUPLICATE_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.cloneRule.duplicateTitle', @@ -47,7 +46,9 @@ export const duplicateRule = async ({ rule }: DuplicateRuleParams): Promise { + test('should convert rule_source params to snake case', () => { + const transformedParams = commonParamsCamelToSnake({ + ...getBaseRuleParams(), + ruleSource: { + type: 'external', + isCustomized: false, + }, + }); + expect(transformedParams).toEqual( + expect.objectContaining({ + rule_source: { + type: 'external', + is_customized: false, + }, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/common_params_camel_to_snake.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/common_params_camel_to_snake.ts new file mode 100644 index 0000000000000..6f98230043e74 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/common_params_camel_to_snake.ts @@ -0,0 +1,47 @@ +/* + * 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 { convertObjectKeysToSnakeCase } from '../../../../../../utils/object_case_converters'; +import type { BaseRuleParams } from '../../../../rule_schema'; +import { migrateLegacyInvestigationFields } from '../../../utils/utils'; + +export const commonParamsCamelToSnake = (params: BaseRuleParams) => { + return { + description: params.description, + risk_score: params.riskScore, + severity: params.severity, + building_block_type: params.buildingBlockType, + namespace: params.namespace, + note: params.note, + license: params.license, + output_index: params.outputIndex, + timeline_id: params.timelineId, + timeline_title: params.timelineTitle, + meta: params.meta, + rule_name_override: params.ruleNameOverride, + timestamp_override: params.timestampOverride, + timestamp_override_fallback_disabled: params.timestampOverrideFallbackDisabled, + investigation_fields: migrateLegacyInvestigationFields(params.investigationFields), + author: params.author, + false_positives: params.falsePositives, + from: params.from, + rule_id: params.ruleId, + max_signals: params.maxSignals, + risk_score_mapping: params.riskScoreMapping, + severity_mapping: params.severityMapping, + threat: params.threat, + to: params.to, + references: params.references, + version: params.version, + exceptions_list: params.exceptionsList, + immutable: params.immutable, + rule_source: convertObjectKeysToSnakeCase(params.ruleSource), + related_integrations: params.relatedIntegrations ?? [], + required_fields: params.requiredFields ?? [], + setup: params.setup ?? '', + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_alerting_rule_to_rule_response.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_alerting_rule_to_rule_response.ts new file mode 100644 index 0000000000000..ab7fb237e64f1 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_alerting_rule_to_rule_response.ts @@ -0,0 +1,26 @@ +/* + * 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 type { SanitizedRule } from '@kbn/alerting-plugin/common'; +import { stringifyZodError } from '@kbn/zod-helpers'; +import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import type { RuleParams } from '../../../../rule_schema'; +import { internalRuleToAPIResponse } from './internal_rule_to_api_response'; +import { RuleResponseValidationError } from '../utils'; + +export function convertAlertingRuleToRuleResponse(rule: SanitizedRule): RuleResponse { + const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(rule)); + + if (!parseResult.success) { + throw new RuleResponseValidationError({ + message: stringifyZodError(parseResult.error), + ruleId: rule.params.ruleId, + }); + } + + return parseResult.data; +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_prebuilt_rule_asset_to_rule_response.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_prebuilt_rule_asset_to_rule_response.ts new file mode 100644 index 0000000000000..0cb42100d4512 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_prebuilt_rule_asset_to_rule_response.ts @@ -0,0 +1,39 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { addEcsToRequiredFields } from '../../../utils/utils'; +import type { PrebuiltRuleAsset } from '../../../../prebuilt_rules'; +import { RULE_DEFAULTS } from '../mergers/apply_rule_defaults'; + +export const convertPrebuiltRuleAssetToRuleResponse = ( + prebuiltRuleAsset: PrebuiltRuleAsset +): RuleResponse => { + const immutable = true; + + const ruleResponseSpecificFields = { + id: uuidv4(), + updated_at: new Date().toISOString(), + updated_by: '', + created_at: new Date().toISOString(), + created_by: '', + immutable, + rule_source: { + type: 'external', + is_customized: false, + }, + revision: 1, + }; + + return RuleResponse.parse({ + ...RULE_DEFAULTS, + ...prebuiltRuleAsset, + required_fields: addEcsToRequiredFields(prebuiltRuleAsset.required_fields), + ...ruleResponseSpecificFields, + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_rule_response_to_alerting_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_rule_response_to_alerting_rule.ts new file mode 100644 index 0000000000000..60a41211a66c5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/convert_rule_response_to_alerting_rule.ts @@ -0,0 +1,210 @@ +/* + * 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 type { UpdateRuleData } from '@kbn/alerting-plugin/server/application/rule/methods/update'; +import type { + RuleResponse, + TypeSpecificCreateProps, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { + transformRuleToAlertAction, + transformRuleToAlertResponseAction, +} from '../../../../../../../common/detection_engine/transform_actions'; +import { + normalizeMachineLearningJobIds, + normalizeThresholdObject, +} from '../../../../../../../common/detection_engine/utils'; +import { assertUnreachable } from '../../../../../../../common/utility_types'; +import { convertObjectKeysToCamelCase } from '../../../../../../utils/object_case_converters'; +import type { RuleParams, TypeSpecificRuleParams } from '../../../../rule_schema'; +import { transformToActionFrequency } from '../../../normalization/rule_actions'; +import { addEcsToRequiredFields } from '../../../utils/utils'; + +/** + * These are the fields that are added to the rule response that are not part of the rule params + */ +type RuntimeFields = + | 'id' + | 'created_at' + | 'updated_at' + | 'created_by' + | 'updated_by' + | 'revision' + | 'execution_summary'; + +export const convertRuleResponseToAlertingRule = ( + rule: Omit +): UpdateRuleData => { + const alertActions = rule.actions.map((action) => transformRuleToAlertAction(action)); + const actions = transformToActionFrequency(alertActions, rule.throttle); + + // Because of Omit Typescript doesn't recognize + // that rule is assignable to TypeSpecificCreateProps despite omitted fields + // are not part of type specific props. So we need to cast here. + const typeSpecificParams = typeSpecificSnakeToCamel(rule as TypeSpecificCreateProps); + + return { + name: rule.name, + tags: rule.tags, + params: { + author: rule.author, + buildingBlockType: rule.building_block_type, + description: rule.description, + ruleId: rule.rule_id, + falsePositives: rule.false_positives, + from: rule.from, + investigationFields: rule.investigation_fields, + immutable: rule.immutable, + ruleSource: convertObjectKeysToCamelCase(rule.rule_source), + license: rule.license, + outputIndex: rule.output_index ?? '', + timelineId: rule.timeline_id, + timelineTitle: rule.timeline_title, + meta: rule.meta, + maxSignals: rule.max_signals, + relatedIntegrations: rule.related_integrations, + requiredFields: addEcsToRequiredFields(rule.required_fields), + riskScore: rule.risk_score, + riskScoreMapping: rule.risk_score_mapping, + ruleNameOverride: rule.rule_name_override, + setup: rule.setup, + severity: rule.severity, + severityMapping: rule.severity_mapping, + threat: rule.threat, + timestampOverride: rule.timestamp_override, + timestampOverrideFallbackDisabled: rule.timestamp_override_fallback_disabled, + to: rule.to, + references: rule.references, + namespace: rule.namespace, + note: rule.note, + version: rule.version, + exceptionsList: rule.exceptions_list, + ...typeSpecificParams, + }, + schedule: { interval: rule.interval }, + actions, + }; +}; + +// Converts params from the snake case API format to the internal camel case format AND applies default values where needed. +// Notice that params.language is possibly undefined for most rule types in the API but we default it to kuery to match +// the legacy API behavior +const typeSpecificSnakeToCamel = (params: TypeSpecificCreateProps): TypeSpecificRuleParams => { + switch (params.type) { + case 'eql': { + return { + type: params.type, + language: params.language, + index: params.index, + dataViewId: params.data_view_id, + query: params.query, + filters: params.filters, + timestampField: params.timestamp_field, + eventCategoryOverride: params.event_category_override, + tiebreakerField: params.tiebreaker_field, + alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression), + }; + } + case 'esql': { + return { + type: params.type, + language: params.language, + query: params.query, + alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression), + }; + } + case 'threat_match': { + return { + type: params.type, + language: params.language ?? 'kuery', + index: params.index, + dataViewId: params.data_view_id, + query: params.query, + filters: params.filters, + savedId: params.saved_id, + threatFilters: params.threat_filters, + threatQuery: params.threat_query, + threatMapping: params.threat_mapping, + threatLanguage: params.threat_language, + threatIndex: params.threat_index, + threatIndicatorPath: params.threat_indicator_path, + concurrentSearches: params.concurrent_searches, + itemsPerSearch: params.items_per_search, + alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression), + }; + } + case 'query': { + return { + type: params.type, + language: params.language ?? 'kuery', + index: params.index, + dataViewId: params.data_view_id, + query: params.query ?? '', + filters: params.filters, + savedId: params.saved_id, + responseActions: params.response_actions?.map((rule) => + transformRuleToAlertResponseAction(rule) + ), + alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression), + }; + } + case 'saved_query': { + return { + type: params.type, + language: params.language ?? 'kuery', + index: params.index, + query: params.query, + filters: params.filters, + savedId: params.saved_id, + dataViewId: params.data_view_id, + responseActions: params.response_actions?.map((rule) => + transformRuleToAlertResponseAction(rule) + ), + alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression), + }; + } + case 'threshold': { + return { + type: params.type, + language: params.language ?? 'kuery', + index: params.index, + dataViewId: params.data_view_id, + query: params.query, + filters: params.filters, + savedId: params.saved_id, + threshold: normalizeThresholdObject(params.threshold), + alertSuppression: params.alert_suppression?.duration + ? { duration: params.alert_suppression.duration } + : undefined, + }; + } + case 'machine_learning': { + return { + type: params.type, + anomalyThreshold: params.anomaly_threshold, + machineLearningJobId: normalizeMachineLearningJobIds(params.machine_learning_job_id), + alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression), + }; + } + case 'new_terms': { + return { + type: params.type, + query: params.query, + newTermsFields: params.new_terms_fields, + historyWindowStart: params.history_window_start, + index: params.index, + filters: params.filters, + language: params.language ?? 'kuery', + dataViewId: params.data_view_id, + alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression), + }; + } + default: { + return assertUnreachable(params); + } + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response.ts new file mode 100644 index 0000000000000..452f59df8dcf9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response.ts @@ -0,0 +1,61 @@ +/* + * 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 type { ResolvedSanitizedRule, SanitizedRule } from '@kbn/alerting-plugin/common'; +import type { RequiredOptional } from '@kbn/zod-helpers'; +import type { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { transformAlertToRuleAction } from '../../../../../../../common/detection_engine/transform_actions'; +import { createRuleExecutionSummary } from '../../../../rule_monitoring'; +import type { RuleParams } from '../../../../rule_schema'; +import { + transformFromAlertThrottle, + transformToActionFrequency, +} from '../../../normalization/rule_actions'; +import { typeSpecificCamelToSnake } from './type_specific_camel_to_snake'; +import { commonParamsCamelToSnake } from './common_params_camel_to_snake'; + +export const internalRuleToAPIResponse = ( + rule: SanitizedRule | ResolvedSanitizedRule +): RequiredOptional => { + const executionSummary = createRuleExecutionSummary(rule); + + const isResolvedRule = (obj: unknown): obj is ResolvedSanitizedRule => { + const outcome = (obj as ResolvedSanitizedRule).outcome; + return outcome != null && outcome !== 'exactMatch'; + }; + + const alertActions = rule.actions.map(transformAlertToRuleAction); + const throttle = transformFromAlertThrottle(rule); + const actions = transformToActionFrequency(alertActions, throttle); + + return { + // saved object properties + outcome: isResolvedRule(rule) ? rule.outcome : undefined, + alias_target_id: isResolvedRule(rule) ? rule.alias_target_id : undefined, + alias_purpose: isResolvedRule(rule) ? rule.alias_purpose : undefined, + // Alerting framework params + id: rule.id, + updated_at: rule.updatedAt.toISOString(), + updated_by: rule.updatedBy ?? 'elastic', + created_at: rule.createdAt.toISOString(), + created_by: rule.createdBy ?? 'elastic', + name: rule.name, + tags: rule.tags, + interval: rule.schedule.interval, + enabled: rule.enabled, + revision: rule.revision, + // Security solution shared rule params + ...commonParamsCamelToSnake(rule.params), + // Type specific security solution rule params + ...typeSpecificCamelToSnake(rule.params), + // Actions + throttle: undefined, + actions, + // Execution summary + execution_summary: executionSummary ?? undefined, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/type_specific_camel_to_snake.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/type_specific_camel_to_snake.test.ts new file mode 100644 index 0000000000000..08e6d3cc64b0a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/type_specific_camel_to_snake.test.ts @@ -0,0 +1,67 @@ +/* + * 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 type { + AlertSuppressionDuration, + AlertSuppressionMissingFieldsStrategy, +} from '../../../../../../../common/api/detection_engine'; +import { getEqlRuleParams } from '../../../../rule_schema/mocks'; +import { typeSpecificCamelToSnake } from './type_specific_camel_to_snake'; + +describe('typeSpecificCamelToSnake', () => { + describe('EQL', () => { + test('should accept EQL params when existing rule type is EQL', () => { + const params = { + timestampField: 'event.created', + eventCategoryOverride: 'event.not_category', + tiebreakerField: 'event.created', + }; + const eqlRule = { ...getEqlRuleParams(), ...params }; + const transformedParams = typeSpecificCamelToSnake(eqlRule); + expect(transformedParams).toEqual( + expect.objectContaining({ + timestamp_field: 'event.created', + event_category_override: 'event.not_category', + tiebreaker_field: 'event.created', + }) + ); + }); + + test('should accept EQL params with suppression in camel case and convert to snake case when rule type is EQL', () => { + const params = { + timestampField: 'event.created', + eventCategoryOverride: 'event.not_category', + tiebreakerField: 'event.created', + alertSuppression: { + groupBy: ['event.type'], + duration: { + value: 10, + unit: 'm', + } as AlertSuppressionDuration, + missingFieldsStrategy: 'suppress' as AlertSuppressionMissingFieldsStrategy, + }, + }; + const eqlRule = { ...getEqlRuleParams(), ...params }; + const transformedParams = typeSpecificCamelToSnake(eqlRule); + expect(transformedParams).toEqual( + expect.objectContaining({ + timestamp_field: 'event.created', + event_category_override: 'event.not_category', + tiebreaker_field: 'event.created', + alert_suppression: { + group_by: ['event.type'], + duration: { + value: 10, + unit: 'm', + } as AlertSuppressionDuration, + missing_fields_strategy: 'suppress', + }, + }) + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/type_specific_camel_to_snake.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/type_specific_camel_to_snake.ts new file mode 100644 index 0000000000000..0808d1921e9bf --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/type_specific_camel_to_snake.ts @@ -0,0 +1,127 @@ +/* + * 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 type { RequiredOptional } from '@kbn/zod-helpers'; +import type { TypeSpecificResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { transformAlertToRuleResponseAction } from '../../../../../../../common/detection_engine/transform_actions'; +import { assertUnreachable } from '../../../../../../../common/utility_types'; +import { convertObjectKeysToSnakeCase } from '../../../../../../utils/object_case_converters'; +import type { TypeSpecificRuleParams } from '../../../../rule_schema'; + +export const typeSpecificCamelToSnake = ( + params: TypeSpecificRuleParams +): RequiredOptional => { + switch (params.type) { + case 'eql': { + return { + type: params.type, + language: params.language, + index: params.index, + data_view_id: params.dataViewId, + query: params.query, + filters: params.filters, + timestamp_field: params.timestampField, + event_category_override: params.eventCategoryOverride, + tiebreaker_field: params.tiebreakerField, + alert_suppression: convertObjectKeysToSnakeCase(params.alertSuppression), + }; + } + case 'esql': { + return { + type: params.type, + language: params.language, + query: params.query, + alert_suppression: convertObjectKeysToSnakeCase(params.alertSuppression), + }; + } + case 'threat_match': { + return { + type: params.type, + language: params.language, + index: params.index, + data_view_id: params.dataViewId, + query: params.query, + filters: params.filters, + saved_id: params.savedId, + threat_filters: params.threatFilters, + threat_query: params.threatQuery, + threat_mapping: params.threatMapping, + threat_language: params.threatLanguage, + threat_index: params.threatIndex, + threat_indicator_path: params.threatIndicatorPath, + concurrent_searches: params.concurrentSearches, + items_per_search: params.itemsPerSearch, + alert_suppression: convertObjectKeysToSnakeCase(params.alertSuppression), + }; + } + case 'query': { + return { + type: params.type, + language: params.language, + index: params.index, + data_view_id: params.dataViewId, + query: params.query, + filters: params.filters, + saved_id: params.savedId, + response_actions: params.responseActions?.map(transformAlertToRuleResponseAction), + alert_suppression: convertObjectKeysToSnakeCase(params.alertSuppression), + }; + } + case 'saved_query': { + return { + type: params.type, + language: params.language, + index: params.index, + query: params.query, + filters: params.filters, + saved_id: params.savedId, + data_view_id: params.dataViewId, + response_actions: params.responseActions?.map(transformAlertToRuleResponseAction), + alert_suppression: convertObjectKeysToSnakeCase(params.alertSuppression), + }; + } + case 'threshold': { + return { + type: params.type, + language: params.language, + index: params.index, + data_view_id: params.dataViewId, + query: params.query, + filters: params.filters, + saved_id: params.savedId, + threshold: params.threshold, + alert_suppression: params.alertSuppression?.duration + ? { duration: params.alertSuppression?.duration } + : undefined, + }; + } + case 'machine_learning': { + return { + type: params.type, + anomaly_threshold: params.anomalyThreshold, + machine_learning_job_id: params.machineLearningJobId, + alert_suppression: convertObjectKeysToSnakeCase(params.alertSuppression), + }; + } + case 'new_terms': { + return { + type: params.type, + query: params.query, + new_terms_fields: params.newTermsFields, + history_window_start: params.historyWindowStart, + index: params.index, + filters: params.filters, + language: params.language, + data_view_id: params.dataViewId, + alert_suppression: convertObjectKeysToSnakeCase(params.alertSuppression), + }; + } + default: { + return assertUnreachable(params); + } + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts index 7aab6640a1b52..5578854ed95b2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts @@ -6,6 +6,7 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { getCreateRulesSchemaMock, @@ -35,7 +36,8 @@ describe('DetectionRulesClient.createCustomRule', () => { rulesClient = rulesClientMock.create(); rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); - detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz); + const savedObjectsClient = savedObjectsClientMock.create(); + detectionRulesClient = createDetectionRulesClient({ rulesClient, mlAuthz, savedObjectsClient }); }); it('should create a rule with the correct parameters and options', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts index fd3ac991a968f..f91c577f3b2a0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts @@ -6,6 +6,7 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { getCreateRulesSchemaMock, @@ -35,7 +36,8 @@ describe('DetectionRulesClient.createPrebuiltRule', () => { rulesClient = rulesClientMock.create(); rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); - detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz); + const savedObjectsClient = savedObjectsClientMock.create(); + detectionRulesClient = createDetectionRulesClient({ rulesClient, mlAuthz, savedObjectsClient }); }); it('creates a rule with the correct parameters and options', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts index 37cb8e0aa709e..166656701f304 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts @@ -6,6 +6,7 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { buildMlAuthz } from '../../../../machine_learning/authz'; import { createDetectionRulesClient } from './detection_rules_client'; import type { IDetectionRulesClient } from './detection_rules_client_interface'; @@ -20,7 +21,8 @@ describe('DetectionRulesClient.deleteRule', () => { beforeEach(() => { rulesClient = rulesClientMock.create(); - detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz); + const savedObjectsClient = savedObjectsClientMock.create(); + detectionRulesClient = createDetectionRulesClient({ rulesClient, mlAuthz, savedObjectsClient }); }); it('should call rulesClient.delete passing the expected ruleId', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts index 474fecc186519..fb9b4f7995c90 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts @@ -6,19 +6,23 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { readRules } from './read_rules'; -import { getCreateRulesSchemaMock } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; -import { getRuleMock } from '../../../routes/__mocks__/request_responses'; -import { getQueryRuleParams } from '../../../rule_schema/mocks'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { + getCreateRulesSchemaMock, + getRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { buildMlAuthz } from '../../../../machine_learning/authz'; import { throwAuthzError } from '../../../../machine_learning/validation'; +import { getRuleMock } from '../../../routes/__mocks__/request_responses'; +import { getQueryRuleParams } from '../../../rule_schema/mocks'; import { createDetectionRulesClient } from './detection_rules_client'; import type { IDetectionRulesClient } from './detection_rules_client_interface'; +import { getRuleByRuleId } from './methods/get_rule_by_rule_id'; jest.mock('../../../../machine_learning/authz'); jest.mock('../../../../machine_learning/validation'); -jest.mock('./read_rules'); +jest.mock('./methods/get_rule_by_rule_id'); describe('DetectionRulesClient.importRule', () => { let rulesClient: ReturnType; @@ -34,21 +38,19 @@ describe('DetectionRulesClient.importRule', () => { version: 1, immutable, }; - const existingRule = getRuleMock({ - ...getQueryRuleParams({ - ruleId: ruleToImport.rule_id, - }), - }); + const existingRule = getRulesSchemaMock(); + existingRule.rule_id = ruleToImport.rule_id; beforeEach(() => { rulesClient = rulesClientMock.create(); rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz); + const savedObjectsClient = savedObjectsClientMock.create(); + detectionRulesClient = createDetectionRulesClient({ rulesClient, mlAuthz, savedObjectsClient }); }); it('calls rulesClient.create with the correct parameters when rule_id does not match an installed rule', async () => { - (readRules as jest.Mock).mockResolvedValue(null); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(null); await detectionRulesClient.importRule({ ruleToImport, overwriteRules: true, @@ -90,7 +92,8 @@ describe('DetectionRulesClient.importRule', () => { describe('when rule_id matches an installed rule', () => { it('calls rulesClient.update with the correct parameters when overwriteRules is true', async () => { - (readRules as jest.Mock).mockResolvedValue(existingRule); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); + await detectionRulesClient.importRule({ ruleToImport, overwriteRules: true, @@ -122,12 +125,9 @@ describe('DetectionRulesClient.importRule', () => { it('ensures overwritten rule DOES NOT preserve fields missed in the imported rule when "overwriteRules" is "true" and matching rule found', async () => { const existingRuleWithTimestampOverride = { ...existingRule, - params: { - ...existingRule.params, - timestamp_override: '2020-01-01T00:00:00Z', - }, + timestamp_override: '2020-01-01T00:00:00Z', }; - (readRules as jest.Mock).mockResolvedValue(existingRuleWithTimestampOverride); + (getRuleByRuleId as jest.Mock).mockResolvedValue(existingRuleWithTimestampOverride); await detectionRulesClient.importRule({ ruleToImport: { @@ -151,7 +151,7 @@ describe('DetectionRulesClient.importRule', () => { }); it('rejects when overwriteRules is false', async () => { - (readRules as jest.Mock).mockResolvedValue(existingRule); + (getRuleByRuleId as jest.Mock).mockResolvedValue(existingRule); await expect( detectionRulesClient.importRule({ ruleToImport, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts index 7f1c219888636..d17b12415642a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts @@ -12,17 +12,20 @@ import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks' import { getCreateMachineLearningRulesSchemaMock, getCreateRulesSchemaMock, + getRulesMlSchemaMock, + getRulesSchemaMock, } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; -import { readRules } from './read_rules'; +import { getRuleByRuleId } from './methods/get_rule_by_rule_id'; import { buildMlAuthz } from '../../../../machine_learning/authz'; import { throwAuthzError } from '../../../../machine_learning/validation'; import { createDetectionRulesClient } from './detection_rules_client'; import type { IDetectionRulesClient } from './detection_rules_client_interface'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; jest.mock('../../../../machine_learning/authz'); jest.mock('../../../../machine_learning/validation'); -jest.mock('./read_rules'); +jest.mock('./methods/get_rule_by_rule_id'); describe('DetectionRulesClient.patchRule', () => { let rulesClient: ReturnType; @@ -32,97 +35,78 @@ describe('DetectionRulesClient.patchRule', () => { beforeEach(() => { rulesClient = rulesClientMock.create(); - detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz); + const savedObjectsClient = savedObjectsClientMock.create(); + detectionRulesClient = createDetectionRulesClient({ rulesClient, mlAuthz, savedObjectsClient }); }); it('calls the rulesClient with expected params', async () => { - const nextParams = getCreateRulesSchemaMock(); - const existingRule = getRuleMock(getQueryRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule update + const rulePatch = getCreateRulesSchemaMock('query-rule-id'); + rulePatch.name = 'new name'; + rulePatch.description = 'new description'; + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await detectionRulesClient.patchRule({ nextParams }); + await detectionRulesClient.patchRule({ rulePatch }); expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ - name: nextParams.name, + name: rulePatch.name, params: expect.objectContaining({ - ruleId: nextParams.rule_id, - description: nextParams.description, + ruleId: rulePatch.rule_id, + description: rulePatch.description, }), }), }) ); }); - it('returns rule enabled: true if the nexParams have enabled: true', async () => { - const nextParams = { ...getCreateRulesSchemaMock(), enabled: true }; - const existingRule = getRuleMock(getQueryRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); - rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + it('enables the rule if the nexParams have enabled: true', async () => { + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + existingRule.enabled = false; + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); - const rule = await detectionRulesClient.patchRule({ nextParams }); + // Mock the rule update + const rulePatch = { ...getCreateRulesSchemaMock(), enabled: true }; - expect(rule.enabled).toBe(true); - }); + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - it('calls the rulesClient with legacy ML params', async () => { - const nextParams = getCreateMachineLearningRulesSchemaMock(); - const existingRule = getRuleMock(getMlRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); - rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); + const rule = await detectionRulesClient.patchRule({ rulePatch }); - await detectionRulesClient.patchRule({ nextParams }); - expect(rulesClient.update).toHaveBeenCalledWith( + expect(rule.enabled).toBe(true); + expect(rulesClient.enable).toHaveBeenCalledWith( expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['typical-ml-job-id'], - }), - }), + id: existingRule.id, }) ); }); - it('calls the rulesClient with new ML params', async () => { - const nextParams = { - ...getCreateMachineLearningRulesSchemaMock(), - machine_learning_job_id: ['new_job_1', 'new_job_2'], - }; - const existingRule = getRuleMock(getMlRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); - rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); - - await detectionRulesClient.patchRule({ nextParams }); + it('disables the rule if the nexParams have enabled: false', async () => { + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + existingRule.enabled = true; + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); - expect(rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['new_job_1', 'new_job_2'], - }), - }), - }) - ); - }); + // Mock the rule update + const rulePatch = { ...getCreateRulesSchemaMock(), enabled: false }; - it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const nextParams = { - ...getCreateRulesSchemaMock(), - enabled: false, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: true, - }; - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await detectionRulesClient.patchRule({ nextParams }); + const rule = await detectionRulesClient.patchRule({ rulePatch }); + expect(rule.enabled).toBe(false); expect(rulesClient.disable).toHaveBeenCalledWith( expect.objectContaining({ id: existingRule.id, @@ -130,23 +114,29 @@ describe('DetectionRulesClient.patchRule', () => { ); }); - it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const nextParams = { - ...getCreateRulesSchemaMock(), - enabled: true, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: false, - }; - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); - rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + it('calls the rulesClient with new ML params', async () => { + // Mock the existing rule + const existingRule = getRulesMlSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); - await detectionRulesClient.patchRule({ nextParams }); + // Mock the rule update + const rulePatch = getCreateMachineLearningRulesSchemaMock(); + rulePatch.anomaly_threshold = 42; + rulePatch.machine_learning_job_id = ['new-job-id']; - expect(rulesClient.enable).toHaveBeenCalledWith( + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw + rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); + + await detectionRulesClient.patchRule({ rulePatch }); + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ - id: existingRule.id, + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: rulePatch.anomaly_threshold, + machineLearningJobId: rulePatch.machine_learning_job_id, + }), + }), }) ); }); @@ -156,21 +146,23 @@ describe('DetectionRulesClient.patchRule', () => { throw new Error('mocked MLAuth error'); }); - const nextParams = { - ...getCreateRulesSchemaMock(), - enabled: true, - }; + const rulePatch = getCreateRulesSchemaMock(); - await expect(detectionRulesClient.patchRule({ nextParams })).rejects.toThrow( + await expect(detectionRulesClient.patchRule({ rulePatch })).rejects.toThrow( 'mocked MLAuth error' ); expect(rulesClient.create).not.toHaveBeenCalled(); }); - describe('regression tests', () => { + describe('actions', () => { it("updates the rule's actions if provided", async () => { - const nextParams = { + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule update + const rulePatch = { ...getCreateRulesSchemaMock(), actions: [ { @@ -183,11 +175,12 @@ describe('DetectionRulesClient.patchRule', () => { }, ], }; - const existingRule = getRuleMock(getQueryRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await detectionRulesClient.patchRule({ nextParams }); + await detectionRulesClient.patchRule({ rulePatch }); expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ @@ -209,12 +202,12 @@ describe('DetectionRulesClient.patchRule', () => { }); it('does not update actions if none are specified', async () => { - const nextParams = getCreateRulesSchemaMock(); - delete nextParams.actions; - const existingRule = getRuleMock(getQueryRuleParams()); + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); existingRule.actions = [ { - actionTypeId: '.slack', + action_type_id: '.slack', id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', params: { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', @@ -222,10 +215,16 @@ describe('DetectionRulesClient.patchRule', () => { group: 'default', }, ]; - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule update + const rulePatch = getCreateRulesSchemaMock(); + delete rulePatch.actions; + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await detectionRulesClient.patchRule({ nextParams }); + await detectionRulesClient.patchRule({ rulePatch }); expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts index ce6043a420907..dfcfc8f7fa393 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts @@ -6,73 +6,110 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; -import type { MlAuthz } from '../../../../machine_learning/authz'; - +import type { SavedObjectsClientContract } from '@kbn/core/server'; import type { RuleResponse } from '../../../../../../common/api/detection_engine/model/rule_schema'; +import { withSecuritySpan } from '../../../../../utils/with_security_span'; +import type { MlAuthz } from '../../../../machine_learning/authz'; +import { createPrebuiltRuleAssetsClient } from '../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; import type { - IDetectionRulesClient, CreateCustomRuleArgs, CreatePrebuiltRuleArgs, - UpdateRuleArgs, - PatchRuleArgs, DeleteRuleArgs, - UpgradePrebuiltRuleArgs, + IDetectionRulesClient, ImportRuleArgs, + PatchRuleArgs, + UpdateRuleArgs, + UpgradePrebuiltRuleArgs, } from './detection_rules_client_interface'; - -import { createCustomRule } from './methods/create_custom_rule'; -import { createPrebuiltRule } from './methods/create_prebuilt_rule'; -import { updateRule } from './methods/update_rule'; -import { patchRule } from './methods/patch_rule'; +import { createRule } from './methods/create_rule'; import { deleteRule } from './methods/delete_rule'; -import { upgradePrebuiltRule } from './methods/upgrade_prebuilt_rule'; import { importRule } from './methods/import_rule'; +import { patchRule } from './methods/patch_rule'; +import { updateRule } from './methods/update_rule'; +import { upgradePrebuiltRule } from './methods/upgrade_prebuilt_rule'; -import { withSecuritySpan } from '../../../../../utils/with_security_span'; +interface DetectionRulesClientParams { + rulesClient: RulesClient; + savedObjectsClient: SavedObjectsClientContract; + mlAuthz: MlAuthz; +} + +export const createDetectionRulesClient = ({ + rulesClient, + mlAuthz, + savedObjectsClient, +}: DetectionRulesClientParams): IDetectionRulesClient => { + const prebuiltRuleAssetClient = createPrebuiltRuleAssetsClient(savedObjectsClient); -export const createDetectionRulesClient = ( - rulesClient: RulesClient, - mlAuthz: MlAuthz -): IDetectionRulesClient => ({ - async createCustomRule(args: CreateCustomRuleArgs): Promise { - return withSecuritySpan('DetectionRulesClient.createCustomRule', async () => { - return createCustomRule(rulesClient, args, mlAuthz); - }); - }, + return { + async createCustomRule(args: CreateCustomRuleArgs): Promise { + return withSecuritySpan('DetectionRulesClient.createCustomRule', async () => { + return createRule({ + rulesClient, + rule: { + ...args.params, + // For backwards compatibility, we default to true if not provided. + // The default enabled value is false for prebuilt rules, and true + // for custom rules. + enabled: args.params.enabled ?? true, + immutable: false, + }, + mlAuthz, + }); + }); + }, - async createPrebuiltRule(args: CreatePrebuiltRuleArgs): Promise { - return withSecuritySpan('DetectionRulesClient.createPrebuiltRule', async () => { - return createPrebuiltRule(rulesClient, args, mlAuthz); - }); - }, + async createPrebuiltRule(args: CreatePrebuiltRuleArgs): Promise { + return withSecuritySpan('DetectionRulesClient.createPrebuiltRule', async () => { + return createRule({ + rulesClient, + rule: { + ...args.params, + immutable: true, + }, + mlAuthz, + }); + }); + }, - async updateRule(args: UpdateRuleArgs): Promise { - return withSecuritySpan('DetectionRulesClient.updateRule', async () => { - return updateRule(rulesClient, args, mlAuthz); - }); - }, + async updateRule({ ruleUpdate }: UpdateRuleArgs): Promise { + return withSecuritySpan('DetectionRulesClient.updateRule', async () => { + return updateRule({ rulesClient, prebuiltRuleAssetClient, mlAuthz, ruleUpdate }); + }); + }, - async patchRule(args: PatchRuleArgs): Promise { - return withSecuritySpan('DetectionRulesClient.patchRule', async () => { - return patchRule(rulesClient, args, mlAuthz); - }); - }, + async patchRule({ rulePatch }: PatchRuleArgs): Promise { + return withSecuritySpan('DetectionRulesClient.patchRule', async () => { + return patchRule({ rulesClient, prebuiltRuleAssetClient, mlAuthz, rulePatch }); + }); + }, - async deleteRule(args: DeleteRuleArgs): Promise { - return withSecuritySpan('DetectionRulesClient.deleteRule', async () => { - return deleteRule(rulesClient, args); - }); - }, + async deleteRule({ ruleId }: DeleteRuleArgs): Promise { + return withSecuritySpan('DetectionRulesClient.deleteRule', async () => { + return deleteRule({ rulesClient, ruleId }); + }); + }, - async upgradePrebuiltRule(args: UpgradePrebuiltRuleArgs): Promise { - return withSecuritySpan('DetectionRulesClient.upgradePrebuiltRule', async () => { - return upgradePrebuiltRule(rulesClient, args, mlAuthz); - }); - }, + async upgradePrebuiltRule({ ruleAsset }: UpgradePrebuiltRuleArgs): Promise { + return withSecuritySpan('DetectionRulesClient.upgradePrebuiltRule', async () => { + return upgradePrebuiltRule({ + rulesClient, + ruleAsset, + mlAuthz, + prebuiltRuleAssetClient, + }); + }); + }, - async importRule(args: ImportRuleArgs): Promise { - return withSecuritySpan('DetectionRulesClient.importRule', async () => { - return importRule(rulesClient, args, mlAuthz); - }); - }, -}); + async importRule(args: ImportRuleArgs): Promise { + return withSecuritySpan('DetectionRulesClient.importRule', async () => { + return importRule({ + rulesClient, + importRulePayload: args, + mlAuthz, + prebuiltRuleAssetClient, + }); + }); + }, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts index 671460b046fea..db9d122e7d912 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts @@ -12,17 +12,20 @@ import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks' import { getCreateMachineLearningRulesSchemaMock, getCreateRulesSchemaMock, + getRulesMlSchemaMock, + getRulesSchemaMock, } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; -import { readRules } from './read_rules'; +import { getRuleByRuleId } from './methods/get_rule_by_rule_id'; import { buildMlAuthz } from '../../../../machine_learning/authz'; import { throwAuthzError } from '../../../../machine_learning/validation'; import { createDetectionRulesClient } from './detection_rules_client'; import type { IDetectionRulesClient } from './detection_rules_client_interface'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; jest.mock('../../../../machine_learning/authz'); jest.mock('../../../../machine_learning/validation'); -jest.mock('./read_rules'); +jest.mock('./methods/get_rule_by_rule_id'); describe('DetectionRulesClient.updateRule', () => { let rulesClient: ReturnType; @@ -32,13 +35,22 @@ describe('DetectionRulesClient.updateRule', () => { beforeEach(() => { rulesClient = rulesClientMock.create(); - detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz); + const savedObjectsClient = savedObjectsClientMock.create(); + detectionRulesClient = createDetectionRulesClient({ rulesClient, mlAuthz, savedObjectsClient }); }); it('calls the rulesClient with expected params', async () => { - const ruleUpdate = getCreateRulesSchemaMock(); - const existingRule = getRuleMock(getQueryRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule update + const ruleUpdate = getCreateRulesSchemaMock('query-rule-id'); + ruleUpdate.name = 'new name'; + ruleUpdate.description = 'new description'; + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); await detectionRulesClient.updateRule({ ruleUpdate }); @@ -56,21 +68,18 @@ describe('DetectionRulesClient.updateRule', () => { ); }); - it('returns rule enabled: true if the nexParams have enabled: true', async () => { - const ruleUpdate = { ...getCreateRulesSchemaMock(), enabled: true }; - const existingRule = getRuleMock(getQueryRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); - rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - - const rule = await detectionRulesClient.updateRule({ ruleUpdate }); - - expect(rule.enabled).toBe(true); - }); + it('calls the rulesClient with new ML params', async () => { + // Mock the existing rule + const existingRule = getRulesMlSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); - it('calls the rulesClient with legacy ML params', async () => { + // Mock the rule update const ruleUpdate = getCreateMachineLearningRulesSchemaMock(); - const existingRule = getRuleMock(getMlRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + ruleUpdate.anomaly_threshold = 42; + ruleUpdate.machine_learning_job_id = ['new-job-id']; + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); await detectionRulesClient.updateRule({ ruleUpdate }); @@ -79,48 +88,26 @@ describe('DetectionRulesClient.updateRule', () => { expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['typical-ml-job-id'], + anomalyThreshold: ruleUpdate.anomaly_threshold, + machineLearningJobId: ruleUpdate.machine_learning_job_id, }), }), }) ); }); - it('calls the rulesClient with new ML params', async () => { - const ruleUpdate = { - ...getCreateMachineLearningRulesSchemaMock(), - machine_learning_job_id: ['new_job_1', 'new_job_2'], - }; - const existingRule = getRuleMock(getMlRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); - rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); + it('disables rule if the rule was enabled and enabled is false', async () => { + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + existingRule.enabled = true; + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); - await detectionRulesClient.updateRule({ ruleUpdate }); + // Mock the rule update + const ruleUpdate = { ...getCreateRulesSchemaMock(), enabled: false }; - expect(rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['new_job_1', 'new_job_2'], - }), - }), - }) - ); - }); - - it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const ruleUpdate = { - ...getCreateRulesSchemaMock(), - enabled: false, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: true, - }; + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); await detectionRulesClient.updateRule({ ruleUpdate }); @@ -131,17 +118,18 @@ describe('DetectionRulesClient.updateRule', () => { ); }); - it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const ruleUpdate = { - ...getCreateRulesSchemaMock(), - enabled: true, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: false, - }; + it('enables rule if the rule was disabled and enabled is true', async () => { + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + existingRule.enabled = false; + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule update + const ruleUpdate = { ...getCreateRulesSchemaMock(), enabled: true }; + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); await detectionRulesClient.updateRule({ ruleUpdate }); @@ -169,8 +157,13 @@ describe('DetectionRulesClient.updateRule', () => { expect(rulesClient.create).not.toHaveBeenCalled(); }); - describe('regression tests', () => { + describe('actions', () => { it("updates the rule's actions if provided", async () => { + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule update const ruleUpdate = { ...getCreateRulesSchemaMock(), actions: [ @@ -184,8 +177,9 @@ describe('DetectionRulesClient.updateRule', () => { }, ], }; - const existingRule = getRuleMock(getQueryRuleParams()); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); await detectionRulesClient.updateRule({ ruleUpdate }); @@ -210,12 +204,12 @@ describe('DetectionRulesClient.updateRule', () => { }); it('updates actions to empty if none are specified', async () => { - const ruleUpdate = getCreateRulesSchemaMock(); - delete ruleUpdate.actions; - const existingRule = getRuleMock(getQueryRuleParams()); + // Mock the existing rule + const existingRule = getRulesSchemaMock(); + (getRuleByRuleId as jest.Mock).mockResolvedValueOnce(existingRule); existingRule.actions = [ { - actionTypeId: '.slack', + action_type_id: '.slack', id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', params: { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', @@ -223,8 +217,14 @@ describe('DetectionRulesClient.updateRule', () => { group: 'default', }, ]; + + // Mock the rule update + const ruleUpdate = getCreateRulesSchemaMock(); + delete ruleUpdate.actions; + + // Mock the rule returned after update; not used for this test directly but + // needed so that the patchRule method does not throw rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - (readRules as jest.Mock).mockResolvedValueOnce(existingRule); await detectionRulesClient.updateRule({ ruleUpdate }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts index 38f3507d2f7ae..7a5ae76371532 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts @@ -10,21 +10,22 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; import { getCreateEqlRuleSchemaMock, getCreateRulesSchemaMock, + getRulesEqlSchemaMock, + getRulesSchemaMock, } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import type { PrebuiltRuleAsset } from '../../../prebuilt_rules'; - -import { readRules } from './read_rules'; +import { getRuleByRuleId } from './methods/get_rule_by_rule_id'; import { getRuleMock } from '../../../routes/__mocks__/request_responses'; import { getEqlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; - import { buildMlAuthz } from '../../../../machine_learning/authz'; import { throwAuthzError } from '../../../../machine_learning/validation'; import { createDetectionRulesClient } from './detection_rules_client'; import type { IDetectionRulesClient } from './detection_rules_client_interface'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; jest.mock('../../../../machine_learning/authz'); jest.mock('../../../../machine_learning/validation'); -jest.mock('./read_rules'); +jest.mock('./methods/get_rule_by_rule_id'); describe('DetectionRulesClient.upgradePrebuiltRule', () => { let rulesClient: ReturnType; @@ -34,7 +35,8 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => { beforeEach(() => { rulesClient = rulesClientMock.create(); - detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz); + const savedObjectsClient = savedObjectsClientMock.create(); + detectionRulesClient = createDetectionRulesClient({ rulesClient, mlAuthz, savedObjectsClient }); }); it('throws if no matching rule_id is found', async () => { @@ -44,7 +46,7 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => { rule_id: 'rule-id', }; - (readRules as jest.Mock).mockResolvedValue(null); + (getRuleByRuleId as jest.Mock).mockResolvedValue(null); await expect(detectionRulesClient.upgradePrebuiltRule({ ruleAsset })).rejects.toThrow( `Failed to find rule ${ruleAsset.rule_id}` ); @@ -80,28 +82,24 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => { rule_id: 'rule-id', }; // Installed version is "query" - const installedRule = getRuleMock({ - ...getQueryRuleParams({ - exceptionsList: [ - { id: 'test_id', list_id: 'hi', type: 'detection', namespace_type: 'agnostic' }, - ], - }), - actions: [ - { - group: 'default', - id: 'test_id', - action_type_id: '.index', - config: { - index: ['index-1', 'index-2'], - }, - }, - ], - ruleId: 'rule-id', - }); + const installedRule = getRulesSchemaMock(); + installedRule.exceptions_list = [ + { id: 'test_id', list_id: 'hi', type: 'detection', namespace_type: 'agnostic' }, + ]; + installedRule.actions = [ + { + group: 'default', + id: 'test_id', + action_type_id: '.index', + params: {}, + }, + ]; + installedRule.rule_id = 'rule-id'; + beforeEach(() => { jest.resetAllMocks(); rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); - (readRules as jest.Mock).mockResolvedValue(installedRule); + (getRuleByRuleId as jest.Mock).mockResolvedValue(installedRule); }); it('deletes the old rule', async () => { @@ -117,16 +115,23 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => { name: ruleAsset.name, tags: ruleAsset.tags, // enabled and actions are kept from original rule - actions: installedRule.actions, + actions: [ + expect.objectContaining({ + actionTypeId: '.index', + group: 'default', + id: 'test_id', + params: {}, + }), + ], enabled: installedRule.enabled, params: expect.objectContaining({ index: ruleAsset.index, description: ruleAsset.description, immutable: true, // exceptions_lists, actions, timeline_id and timeline_title are maintained - timelineTitle: installedRule.params.timelineTitle, - timelineId: installedRule.params.timelineId, - exceptionsList: installedRule.params.exceptionsList, + timelineTitle: installedRule.timeline_title, + timelineId: installedRule.timeline_id, + exceptionsList: installedRule.exceptions_list, }), }), options: { @@ -147,11 +152,9 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => { rule_id: 'rule-id', }; // Installed version is "eql" - const installedRule = getRuleMock({ - ...getEqlRuleParams(), - }); + const installedRule = getRulesEqlSchemaMock(); beforeEach(() => { - (readRules as jest.Mock).mockResolvedValue(installedRule); + (getRuleByRuleId as jest.Mock).mockResolvedValue(installedRule); }); it('patches the existing rule with the new params from the rule asset', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface.ts index 34c39153206b1..d7b45f83e8bf8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface.ts @@ -38,7 +38,7 @@ export interface UpdateRuleArgs { } export interface PatchRuleArgs { - nextParams: RulePatchProps; + rulePatch: RulePatchProps; } export interface DeleteRuleArgs { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_defaults.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_defaults.ts new file mode 100644 index 0000000000000..837df0b3b2f1f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_defaults.ts @@ -0,0 +1,185 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import type { + RuleCreateProps, + RuleSource, + TypeSpecificCreateProps, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { + DEFAULT_INDICATOR_SOURCE_PATH, + DEFAULT_MAX_SIGNALS, +} from '../../../../../../../common/constants'; +import { + normalizeMachineLearningJobIds, + normalizeThresholdObject, +} from '../../../../../../../common/detection_engine/utils'; +import { assertUnreachable } from '../../../../../../../common/utility_types'; +import { addEcsToRequiredFields } from '../../../utils/utils'; + +export const RULE_DEFAULTS = { + enabled: false, + risk_score_mapping: [], + severity_mapping: [], + interval: '5m' as const, + to: 'now' as const, + from: 'now-6m' as const, + exceptions_list: [], + false_positives: [], + max_signals: DEFAULT_MAX_SIGNALS, + actions: [], + related_integrations: [], + required_fields: [], + setup: '', + references: [], + threat: [], + tags: [], + author: [], + output_index: '', + version: 1, +}; + +export function applyRuleDefaults(rule: RuleCreateProps & { immutable?: boolean }) { + const typeSpecificParams = setTypeSpecificDefaults(rule); + const immutable = rule.immutable ?? false; + + return { + ...RULE_DEFAULTS, + ...rule, + ...typeSpecificParams, + rule_id: rule.rule_id ?? uuidv4(), + immutable, + rule_source: convertImmutableToRuleSource(immutable), + required_fields: addEcsToRequiredFields(rule.required_fields), + }; +} + +const convertImmutableToRuleSource = (immutable: boolean): RuleSource => { + if (immutable) { + return { + type: 'external', + is_customized: false, + }; + } + + return { + type: 'internal', + }; +}; + +export const setTypeSpecificDefaults = (props: TypeSpecificCreateProps) => { + switch (props.type) { + case 'eql': { + return { + type: props.type, + language: props.language, + index: props.index, + data_view_id: props.data_view_id, + query: props.query, + filters: props.filters, + timestamp_field: props.timestamp_field, + event_category_override: props.event_category_override, + tiebreaker_field: props.tiebreaker_field, + alert_suppression: props.alert_suppression, + }; + } + case 'esql': { + return { + type: props.type, + language: props.language, + query: props.query, + alert_suppression: props.alert_suppression, + }; + } + case 'threat_match': { + return { + type: props.type, + language: props.language ?? 'kuery', + index: props.index, + data_view_id: props.data_view_id, + query: props.query, + filters: props.filters, + saved_id: props.saved_id, + threat_filters: props.threat_filters, + threat_query: props.threat_query, + threat_mapping: props.threat_mapping, + threat_language: props.threat_language, + threat_index: props.threat_index, + threat_indicator_path: props.threat_indicator_path ?? DEFAULT_INDICATOR_SOURCE_PATH, + concurrent_searches: props.concurrent_searches, + items_per_search: props.items_per_search, + alert_suppression: props.alert_suppression, + }; + } + case 'query': { + return { + type: props.type, + language: props.language ?? 'kuery', + index: props.index, + data_view_id: props.data_view_id, + query: props.query ?? '', + filters: props.filters, + saved_id: props.saved_id, + response_actions: props.response_actions, + alert_suppression: props.alert_suppression, + }; + } + case 'saved_query': { + return { + type: props.type, + language: props.language ?? 'kuery', + index: props.index, + query: props.query, + filters: props.filters, + saved_id: props.saved_id, + data_view_id: props.data_view_id, + response_actions: props.response_actions, + alert_suppression: props.alert_suppression, + }; + } + case 'threshold': { + return { + type: props.type, + language: props.language ?? 'kuery', + index: props.index, + data_view_id: props.data_view_id, + query: props.query, + filters: props.filters, + saved_id: props.saved_id, + threshold: normalizeThresholdObject(props.threshold), + alert_suppression: props.alert_suppression?.duration + ? { duration: props.alert_suppression.duration } + : undefined, + }; + } + case 'machine_learning': { + return { + type: props.type, + anomaly_threshold: props.anomaly_threshold, + machine_learning_job_id: normalizeMachineLearningJobIds(props.machine_learning_job_id), + alert_suppression: props.alert_suppression, + }; + } + case 'new_terms': { + return { + type: props.type, + query: props.query, + new_terms_fields: props.new_terms_fields, + history_window_start: props.history_window_start, + index: props.index, + filters: props.filters, + language: props.language ?? 'kuery', + data_view_id: props.data_view_id, + alert_suppression: props.alert_suppression, + }; + } + default: { + return assertUnreachable(props); + } + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts new file mode 100644 index 0000000000000..49592aff28f95 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts @@ -0,0 +1,456 @@ +/* + * 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 type { + AlertSuppressionDuration, + PatchRuleRequestBody, +} from '../../../../../../../common/api/detection_engine'; +import { + getEsqlRuleSchemaMock, + getRulesEqlSchemaMock, + getRulesMlSchemaMock, + getRulesNewTermsSchemaMock, + getRulesSchemaMock, + getRulesThresholdSchemaMock, + getSavedQuerySchemaMock, + getThreatMatchingSchemaMock, +} from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { createPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client'; +import { applyRulePatch } from './apply_rule_patch'; + +const prebuiltRuleAssetClient = createPrebuiltRuleAssetsClient(); + +describe('applyRulePatch', () => { + describe('EQL', () => { + test('should accept EQL params when existing rule type is EQL', async () => { + const rulePatch = { + timestamp_field: 'event.created', + event_category_override: 'event.not_category', + tiebreaker_field: 'event.created', + }; + const existingRule = getRulesEqlSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + timestamp_field: 'event.created', + event_category_override: 'event.not_category', + tiebreaker_field: 'event.created', + }) + ); + }); + test('should accept EQL params with suppression in snake case and convert to camel case when rule type is EQL', async () => { + const rulePatch = { + timestamp_field: 'event.created', + event_category_override: 'event.not_category', + tiebreaker_field: 'event.created', + alert_suppression: { + group_by: ['event.type'], + duration: { + value: 10, + unit: 'm', + } as AlertSuppressionDuration, + missing_fields_strategy: 'suppress', + }, + }; + const existingRule = getRulesEqlSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + timestamp_field: 'event.created', + event_category_override: 'event.not_category', + tiebreaker_field: 'event.created', + alert_suppression: { + group_by: ['event.type'], + duration: { + value: 10, + unit: 'm', + }, + missing_fields_strategy: 'suppress', + }, + }) + ); + }); + test('should reject invalid EQL params when existing rule type is EQL', async () => { + const rulePatch = { + timestamp_field: 1, + event_category_override: 1, + tiebreaker_field: 1, + } as PatchRuleRequestBody; + const existingRule = getRulesEqlSchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError( + 'event_category_override: Expected string, received number, tiebreaker_field: Expected string, received number, timestamp_field: Expected string, received number' + ); + }); + test('should reject EQL params with invalid suppression group_by field', async () => { + const rulePatch = { + timestamp_field: 'event.created', + event_category_override: 'event.not_category', + tiebreaker_field: 'event.created', + alert_suppression: { + group_by: 'event.type', + duration: { + value: 10, + unit: 'm', + } as AlertSuppressionDuration, + missing_fields_strategy: 'suppress', + }, + }; + const existingRule = getRulesEqlSchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError('alert_suppression.group_by: Expected array, received string'); + }); + }); + + test('should accept threat match params when existing rule type is threat match', async () => { + const rulePatch = { + threat_indicator_path: 'my.indicator', + threat_query: 'test-query', + }; + const existingRule = getThreatMatchingSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + threat_indicator_path: 'my.indicator', + threat_query: 'test-query', + }) + ); + }); + + test('should reject invalid threat match params when existing rule type is threat match', async () => { + const rulePatch = { + threat_indicator_path: 1, + threat_query: 1, + } as PatchRuleRequestBody; + const existingRule = getThreatMatchingSchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError( + 'threat_query: Expected string, received number, threat_indicator_path: Expected string, received number' + ); + }); + + test('should accept query params when existing rule type is query', async () => { + const rulePatch = { + index: ['new-test-index'], + language: 'lucene', + } as PatchRuleRequestBody; + const existingRule = getRulesSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + index: ['new-test-index'], + language: 'lucene', + }) + ); + }); + + test('should reject invalid query params when existing rule type is query', async () => { + const rulePatch = { + index: [1], + language: 'non-language', + } as PatchRuleRequestBody; + const existingRule = getRulesSchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError( + "index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'" + ); + }); + + test('should accept saved query params when existing rule type is saved query', async () => { + const rulePatch = { + index: ['new-test-index'], + language: 'lucene', + } as PatchRuleRequestBody; + const existingRule = getSavedQuerySchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + index: ['new-test-index'], + language: 'lucene', + }) + ); + }); + + test('should reject invalid saved query params when existing rule type is saved query', async () => { + const rulePatch = { + index: [1], + language: 'non-language', + } as PatchRuleRequestBody; + const existingRule = getSavedQuerySchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError( + "index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'" + ); + }); + + test('should accept threshold params when existing rule type is threshold', async () => { + const rulePatch = { + threshold: { + field: ['host.name'], + value: 107, + }, + }; + const existingRule = getRulesThresholdSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + threshold: { + field: ['host.name'], + value: 107, + }, + }) + ); + }); + + test('should reject invalid threshold params when existing rule type is threshold', async () => { + const rulePatch = { + threshold: { + field: ['host.name'], + value: 'invalid', + }, + } as PatchRuleRequestBody; + const existingRule = getRulesThresholdSchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError('threshold.value: Expected number, received string'); + }); + + test('should accept ES|QL alerts suppression params', async () => { + const rulePatch = { + alert_suppression: { + group_by: ['agent.name'], + duration: { value: 4, unit: 'h' as const }, + missing_fields_strategy: 'doNotSuppress' as const, + }, + }; + const existingRule = getEsqlRuleSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'doNotSuppress', + duration: { value: 4, unit: 'h' }, + }, + }) + ); + }); + + test('should accept threshold alerts suppression params', async () => { + const rulePatch = { + alert_suppression: { + duration: { value: 4, unit: 'h' as const }, + }, + }; + const existingRule = getRulesThresholdSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + alert_suppression: { + duration: { value: 4, unit: 'h' }, + }, + }) + ); + }); + + test('should accept threat_match alerts suppression params', async () => { + const rulePatch = { + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'suppress' as const, + }, + }; + const existingRule = getThreatMatchingSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'suppress', + }, + }) + ); + }); + + test('should accept new_terms alerts suppression params', async () => { + const rulePatch = { + alert_suppression: { + group_by: ['agent.name'], + duration: { value: 4, unit: 'h' as const }, + missing_fields_strategy: 'suppress' as const, + }, + }; + const existingRule = getRulesNewTermsSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'suppress', + duration: { value: 4, unit: 'h' }, + }, + }) + ); + }); + + describe('machine learning rules', () => { + test('should accept machine learning params when existing rule type is machine learning', async () => { + const rulePatch = { + anomaly_threshold: 5, + }; + const existingRule = getRulesMlSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + anomaly_threshold: 5, + }) + ); + }); + + test('should reject invalid machine learning params when existing rule type is machine learning', async () => { + const rulePatch = { + anomaly_threshold: 'invalid', + } as PatchRuleRequestBody; + const existingRule = getRulesMlSchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError('anomaly_threshold: Expected number, received string'); + }); + + it('accepts suppression params', async () => { + const rulePatch = { + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'suppress' as const, + }, + }; + const existingRule = getRulesMlSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + + expect(patchedRule).toEqual( + expect.objectContaining({ + alert_suppression: { + group_by: ['agent.name'], + missing_fields_strategy: 'suppress', + }, + }) + ); + }); + }); + + test('should accept new terms params when existing rule type is new terms', async () => { + const rulePatch = { + new_terms_fields: ['event.new_field'], + }; + const existingRule = getRulesNewTermsSchemaMock(); + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + new_terms_fields: ['event.new_field'], + }) + ); + }); + + test('should reject invalid new terms params when existing rule type is new terms', async () => { + const rulePatch = { + new_terms_fields: 'invalid', + } as PatchRuleRequestBody; + const existingRule = getRulesNewTermsSchemaMock(); + await expect( + applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }) + ).rejects.toThrowError('new_terms_fields: Expected array, received string'); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts new file mode 100644 index 0000000000000..9d02cd8dbb9df --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts @@ -0,0 +1,334 @@ +/* + * 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 { BadRequestError } from '@kbn/securitysolution-es-utils'; +import { stringifyZodError } from '@kbn/zod-helpers'; +import type { + EqlRule, + EqlRuleResponseFields, + EsqlRule, + EsqlRuleResponseFields, + MachineLearningRule, + MachineLearningRuleResponseFields, + NewTermsRule, + NewTermsRuleResponseFields, + QueryRule, + QueryRuleResponseFields, + RuleResponse, + SavedQueryRule, + SavedQueryRuleResponseFields, + ThreatMatchRule, + ThreatMatchRuleResponseFields, + ThresholdRule, + ThresholdRuleResponseFields, + TypeSpecificResponse, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { + EqlRulePatchFields, + EsqlRulePatchFields, + MachineLearningRulePatchFields, + NewTermsRulePatchFields, + QueryRulePatchFields, + SavedQueryRulePatchFields, + ThreatMatchRulePatchFields, + ThresholdRulePatchFields, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import type { PatchRuleRequestBody } from '../../../../../../../common/api/detection_engine/rule_management'; +import { + normalizeMachineLearningJobIds, + normalizeThresholdObject, +} from '../../../../../../../common/detection_engine/utils'; +import { assertUnreachable } from '../../../../../../../common/utility_types'; +import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; +import { addEcsToRequiredFields } from '../../../utils/utils'; +import { calculateRuleSource } from './rule_source/calculate_rule_source'; + +interface ApplyRulePatchProps { + prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; + existingRule: RuleResponse; + rulePatch: PatchRuleRequestBody; +} + +// eslint-disable-next-line complexity +export const applyRulePatch = async ({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, +}: ApplyRulePatchProps): Promise => { + const typeSpecificParams = patchTypeSpecificParams(rulePatch, existingRule); + + const nextRule: RuleResponse = { + // Keep existing values for these fields + id: existingRule.id, + rule_id: existingRule.rule_id, + revision: existingRule.revision, + immutable: existingRule.immutable, + rule_source: existingRule.rule_source, + updated_at: existingRule.updated_at, + updated_by: existingRule.updated_by, + created_at: existingRule.created_at, + created_by: existingRule.created_by, + + // Update values for these fields + enabled: rulePatch.enabled ?? existingRule.enabled, + name: rulePatch.name ?? existingRule.name, + tags: rulePatch.tags ?? existingRule.tags, + author: rulePatch.author ?? existingRule.author, + building_block_type: rulePatch.building_block_type ?? existingRule.building_block_type, + description: rulePatch.description ?? existingRule.description, + false_positives: rulePatch.false_positives ?? existingRule.false_positives, + investigation_fields: rulePatch.investigation_fields ?? existingRule.investigation_fields, + from: rulePatch.from ?? existingRule.from, + license: rulePatch.license ?? existingRule.license, + output_index: rulePatch.output_index ?? existingRule.output_index, + timeline_id: rulePatch.timeline_id ?? existingRule.timeline_id, + timeline_title: rulePatch.timeline_title ?? existingRule.timeline_title, + meta: rulePatch.meta ?? existingRule.meta, + max_signals: rulePatch.max_signals ?? existingRule.max_signals, + related_integrations: rulePatch.related_integrations ?? existingRule.related_integrations, + required_fields: addEcsToRequiredFields(rulePatch.required_fields), + risk_score: rulePatch.risk_score ?? existingRule.risk_score, + risk_score_mapping: rulePatch.risk_score_mapping ?? existingRule.risk_score_mapping, + rule_name_override: rulePatch.rule_name_override ?? existingRule.rule_name_override, + setup: rulePatch.setup ?? existingRule.setup, + severity: rulePatch.severity ?? existingRule.severity, + severity_mapping: rulePatch.severity_mapping ?? existingRule.severity_mapping, + threat: rulePatch.threat ?? existingRule.threat, + timestamp_override: rulePatch.timestamp_override ?? existingRule.timestamp_override, + timestamp_override_fallback_disabled: + rulePatch.timestamp_override_fallback_disabled ?? + existingRule.timestamp_override_fallback_disabled, + to: rulePatch.to ?? existingRule.to, + references: rulePatch.references ?? existingRule.references, + namespace: rulePatch.namespace ?? existingRule.namespace, + note: rulePatch.note ?? existingRule.note, + version: rulePatch.version ?? existingRule.version, + exceptions_list: rulePatch.exceptions_list ?? existingRule.exceptions_list, + interval: rulePatch.interval ?? existingRule.interval, + throttle: rulePatch.throttle ?? existingRule.throttle, + actions: rulePatch.actions ?? existingRule.actions, + ...typeSpecificParams, + }; + + nextRule.rule_source = await calculateRuleSource({ + rule: nextRule, + prebuiltRuleAssetClient, + }); + + return nextRule; +}; + +const patchEqlParams = ( + rulePatch: EqlRulePatchFields, + existingRule: EqlRule +): EqlRuleResponseFields => { + return { + type: existingRule.type, + language: rulePatch.language ?? existingRule.language, + index: rulePatch.index ?? existingRule.index, + data_view_id: rulePatch.data_view_id ?? existingRule.data_view_id, + query: rulePatch.query ?? existingRule.query, + filters: rulePatch.filters ?? existingRule.filters, + timestamp_field: rulePatch.timestamp_field ?? existingRule.timestamp_field, + event_category_override: + rulePatch.event_category_override ?? existingRule.event_category_override, + tiebreaker_field: rulePatch.tiebreaker_field ?? existingRule.tiebreaker_field, + alert_suppression: rulePatch.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +const patchEsqlParams = ( + rulePatch: EsqlRulePatchFields, + existingRule: EsqlRule +): EsqlRuleResponseFields => { + return { + type: existingRule.type, + language: rulePatch.language ?? existingRule.language, + query: rulePatch.query ?? existingRule.query, + alert_suppression: rulePatch.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +const patchThreatMatchParams = ( + rulePatch: ThreatMatchRulePatchFields, + existingRule: ThreatMatchRule +): ThreatMatchRuleResponseFields => { + return { + type: existingRule.type, + language: rulePatch.language ?? existingRule.language, + index: rulePatch.index ?? existingRule.index, + data_view_id: rulePatch.data_view_id ?? existingRule.data_view_id, + query: rulePatch.query ?? existingRule.query, + filters: rulePatch.filters ?? existingRule.filters, + saved_id: rulePatch.saved_id ?? existingRule.saved_id, + threat_filters: rulePatch.threat_filters ?? existingRule.threat_filters, + threat_query: rulePatch.threat_query ?? existingRule.threat_query, + threat_mapping: rulePatch.threat_mapping ?? existingRule.threat_mapping, + threat_language: rulePatch.threat_language ?? existingRule.threat_language, + threat_index: rulePatch.threat_index ?? existingRule.threat_index, + threat_indicator_path: rulePatch.threat_indicator_path ?? existingRule.threat_indicator_path, + concurrent_searches: rulePatch.concurrent_searches ?? existingRule.concurrent_searches, + items_per_search: rulePatch.items_per_search ?? existingRule.items_per_search, + alert_suppression: rulePatch.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +const patchQueryParams = ( + rulePatch: QueryRulePatchFields, + existingRule: QueryRule +): QueryRuleResponseFields => { + return { + type: existingRule.type, + language: rulePatch.language ?? existingRule.language, + index: rulePatch.index ?? existingRule.index, + data_view_id: rulePatch.data_view_id ?? existingRule.data_view_id, + query: rulePatch.query ?? existingRule.query, + filters: rulePatch.filters ?? existingRule.filters, + saved_id: rulePatch.saved_id ?? existingRule.saved_id, + response_actions: rulePatch.response_actions ?? existingRule.response_actions, + alert_suppression: rulePatch.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +const patchSavedQueryParams = ( + rulePatch: SavedQueryRulePatchFields, + existingRule: SavedQueryRule +): SavedQueryRuleResponseFields => { + return { + type: existingRule.type, + language: rulePatch.language ?? existingRule.language, + index: rulePatch.index ?? existingRule.index, + data_view_id: rulePatch.data_view_id ?? existingRule.data_view_id, + query: rulePatch.query ?? existingRule.query, + filters: rulePatch.filters ?? existingRule.filters, + saved_id: rulePatch.saved_id ?? existingRule.saved_id, + response_actions: rulePatch.response_actions ?? existingRule.response_actions, + alert_suppression: rulePatch.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +const patchThresholdParams = ( + rulePatch: ThresholdRulePatchFields, + existingRule: ThresholdRule +): ThresholdRuleResponseFields => { + return { + type: existingRule.type, + language: rulePatch.language ?? existingRule.language, + index: rulePatch.index ?? existingRule.index, + data_view_id: rulePatch.data_view_id ?? existingRule.data_view_id, + query: rulePatch.query ?? existingRule.query, + filters: rulePatch.filters ?? existingRule.filters, + saved_id: rulePatch.saved_id ?? existingRule.saved_id, + threshold: rulePatch.threshold + ? normalizeThresholdObject(rulePatch.threshold) + : existingRule.threshold, + alert_suppression: rulePatch.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +const patchMachineLearningParams = ( + params: MachineLearningRulePatchFields, + existingRule: MachineLearningRule +): MachineLearningRuleResponseFields => { + return { + type: existingRule.type, + anomaly_threshold: params.anomaly_threshold ?? existingRule.anomaly_threshold, + machine_learning_job_id: params.machine_learning_job_id + ? normalizeMachineLearningJobIds(params.machine_learning_job_id) + : existingRule.machine_learning_job_id, + alert_suppression: params.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +const patchNewTermsParams = ( + params: NewTermsRulePatchFields, + existingRule: NewTermsRule +): NewTermsRuleResponseFields => { + return { + type: existingRule.type, + language: params.language ?? existingRule.language, + index: params.index ?? existingRule.index, + data_view_id: params.data_view_id ?? existingRule.data_view_id, + query: params.query ?? existingRule.query, + filters: params.filters ?? existingRule.filters, + new_terms_fields: params.new_terms_fields ?? existingRule.new_terms_fields, + history_window_start: params.history_window_start ?? existingRule.history_window_start, + alert_suppression: params.alert_suppression ?? existingRule.alert_suppression, + }; +}; + +export const patchTypeSpecificParams = ( + params: PatchRuleRequestBody, + existingRule: RuleResponse +): TypeSpecificResponse => { + // Here we do the validation of patch params by rule type to ensure that the fields that are + // passed in to patch are of the correct type, e.g. `query` is a string. Since the combined patch schema + // is a union of types where everything is optional, it's hard to do the validation before we know the rule type - + // a patch request that defines `event_category_override` as a number would not be assignable to the EQL patch schema, + // but would be assignable to the other rule types since they don't specify `event_category_override`. + switch (existingRule.type) { + case 'eql': { + const result = EqlRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchEqlParams(result.data, existingRule); + } + case 'esql': { + const result = EsqlRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchEsqlParams(result.data, existingRule); + } + case 'threat_match': { + const result = ThreatMatchRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchThreatMatchParams(result.data, existingRule); + } + case 'query': { + const result = QueryRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchQueryParams(result.data, existingRule); + } + case 'saved_query': { + const result = SavedQueryRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchSavedQueryParams(result.data, existingRule); + } + case 'threshold': { + const result = ThresholdRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchThresholdParams(result.data, existingRule); + } + case 'machine_learning': { + const result = MachineLearningRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchMachineLearningParams(result.data, existingRule); + } + case 'new_terms': { + const result = NewTermsRulePatchFields.safeParse(params); + if (!result.success) { + throw new BadRequestError(stringifyZodError(result.error)); + } + return patchNewTermsParams(result.data, existingRule); + } + default: { + return assertUnreachable(existingRule); + } + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_update.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_update.ts new file mode 100644 index 0000000000000..b911e66a1fc45 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_update.ts @@ -0,0 +1,52 @@ +/* + * 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 type { + RuleResponse, + RuleUpdateProps, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; +import { applyRuleDefaults } from './apply_rule_defaults'; +import { calculateRuleSource } from './rule_source/calculate_rule_source'; + +interface ApplyRuleUpdateProps { + prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; + existingRule: RuleResponse; + ruleUpdate: RuleUpdateProps; +} + +export const applyRuleUpdate = async ({ + prebuiltRuleAssetClient, + existingRule, + ruleUpdate, +}: ApplyRuleUpdateProps): Promise => { + const nextRule: RuleResponse = { + ...applyRuleDefaults(ruleUpdate), + + // Use existing values + enabled: ruleUpdate.enabled ?? existingRule.enabled, + version: ruleUpdate.version ?? existingRule.version, + + // Always keep existing values for these fields + id: existingRule.id, + rule_id: existingRule.rule_id, + revision: existingRule.revision, + immutable: existingRule.immutable, + rule_source: existingRule.rule_source, + updated_at: existingRule.updated_at, + updated_by: existingRule.updated_by, + created_at: existingRule.created_at, + created_by: existingRule.created_by, + }; + + nextRule.rule_source = await calculateRuleSource({ + rule: nextRule, + prebuiltRuleAssetClient, + }); + + return nextRule; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_is_customized.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_is_customized.ts new file mode 100644 index 0000000000000..4f9bb4a060f6f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_is_customized.ts @@ -0,0 +1,33 @@ +/* + * 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 type { RuleResponse } from '../../../../../../../../common/api/detection_engine'; +import { MissingVersion } from '../../../../../../../../common/api/detection_engine'; +import type { PrebuiltRuleAsset } from '../../../../../prebuilt_rules'; +import { calculateRuleFieldsDiff } from '../../../../../prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff'; +import { convertRuleToDiffable } from '../../../../../prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable'; +import { convertPrebuiltRuleAssetToRuleResponse } from '../../converters/convert_prebuilt_rule_asset_to_rule_response'; + +export function calculateIsCustomized( + baseRule: PrebuiltRuleAsset | undefined, + nextRule: RuleResponse +) { + if (baseRule == null) { + // If the base version is missing, we consider the rule to be customized + return true; + } + + const baseRuleWithDefaults = convertPrebuiltRuleAssetToRuleResponse(baseRule); + + const fieldsDiff = calculateRuleFieldsDiff({ + base_version: MissingVersion, + current_version: convertRuleToDiffable(baseRuleWithDefaults), + target_version: convertRuleToDiffable(nextRule), + }); + + return Object.values(fieldsDiff).some((diff) => diff.has_update); +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.test.ts new file mode 100644 index 0000000000000..e44c69d2705d5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.test.ts @@ -0,0 +1,112 @@ +/* + * 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 { createPrebuiltRuleAssetsClient } from '../../../../../prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client'; +import { applyRuleDefaults } from '../apply_rule_defaults'; +import { calculateRuleSource } from './calculate_rule_source'; + +const prebuiltRuleAssetClient = createPrebuiltRuleAssetsClient(); + +const getSampleRuleAsset = () => { + return applyRuleDefaults({ + rule_id: 'test-rule-id', + name: 'Test rule', + description: 'Test description', + type: 'query', + query: 'user.name: root or user.name: admin', + severity: 'high', + risk_score: 55, + }); +}; + +const getSampleRule = () => { + return { + ...getSampleRuleAsset(), + id: 'test-rule-id', + updated_at: '2021-01-01T00:00:00Z', + updated_by: 'test-user', + created_at: '2021-01-01T00:00:00Z', + created_by: 'test-user', + revision: 1, + }; +}; + +describe('calculateRuleSource', () => { + it('returns an internal rule source when the rule is not prebuilt', async () => { + const rule = getSampleRule(); + rule.immutable = false; + + const result = await calculateRuleSource({ + prebuiltRuleAssetClient, + rule, + }); + expect(result).toEqual({ + type: 'internal', + }); + }); + + it('returns an external rule source with customized false when the rule is prebuilt', async () => { + const rule = getSampleRule(); + rule.immutable = true; + + const baseRule = getSampleRuleAsset(); + prebuiltRuleAssetClient.fetchAssetsByVersion.mockResolvedValueOnce([baseRule]); + + const result = await calculateRuleSource({ + prebuiltRuleAssetClient, + rule, + }); + expect(result).toEqual( + expect.objectContaining({ + type: 'external', + is_customized: false, + }) + ); + }); + + it('returns is_customized true when the rule is prebuilt and has been customized', async () => { + const rule = getSampleRule(); + rule.immutable = true; + rule.name = 'Updated name'; + + const baseRule = getSampleRuleAsset(); + prebuiltRuleAssetClient.fetchAssetsByVersion.mockResolvedValueOnce([baseRule]); + + const result = await calculateRuleSource({ + prebuiltRuleAssetClient, + rule, + }); + expect(result).toEqual( + expect.objectContaining({ + type: 'external', + is_customized: true, + }) + ); + }); + + it('returns is_customized false when the rule has only changes to revision, updated_at, updated_by', async () => { + const rule = getSampleRule(); + rule.immutable = true; + rule.revision = 5; + rule.updated_at = '2024-01-01T00:00:00Z'; + rule.updated_by = 'new-user'; + + const baseRule = getSampleRuleAsset(); + prebuiltRuleAssetClient.fetchAssetsByVersion.mockResolvedValueOnce([baseRule]); + + const result = await calculateRuleSource({ + prebuiltRuleAssetClient, + rule, + }); + expect(result).toEqual( + expect.objectContaining({ + type: 'external', + is_customized: false, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.ts new file mode 100644 index 0000000000000..742cd20544a60 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.ts @@ -0,0 +1,47 @@ +/* + * 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 type { + RuleResponse, + RuleSource, +} from '../../../../../../../../common/api/detection_engine/model/rule_schema'; +import type { PrebuiltRuleAsset } from '../../../../../prebuilt_rules'; +import type { IPrebuiltRuleAssetsClient } from '../../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; +import { calculateIsCustomized } from './calculate_is_customized'; + +interface CalculateRuleSourceProps { + prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; + rule: RuleResponse; +} + +export async function calculateRuleSource({ + prebuiltRuleAssetClient, + rule, +}: CalculateRuleSourceProps): Promise { + if (rule.immutable) { + // This is a prebuilt rule and, despite the name, they are not immutable. So + // we need to recalculate `ruleSource.isCustomized` based on the rule's contents. + const prebuiltRulesResponse = await prebuiltRuleAssetClient.fetchAssetsByVersion([ + { + rule_id: rule.rule_id, + version: rule.version, + }, + ]); + const baseRule: PrebuiltRuleAsset | undefined = prebuiltRulesResponse.at(0); + + const isCustomized = calculateIsCustomized(baseRule, rule); + + return { + type: 'external', + is_customized: isCustomized, + }; + } + + return { + type: 'internal', + }; +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/__mocks__/get_rule_by_rule_id.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/__mocks__/get_rule_by_rule_id.ts new file mode 100644 index 0000000000000..251cd7f699195 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/__mocks__/get_rule_by_rule_id.ts @@ -0,0 +1,13 @@ +/* + * 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 type { RuleResponse } from '../../../../../../../../common/api/detection_engine'; +import { getRulesSchemaMock } from '../../../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock'; + +export const getRuleByRuleId = jest + .fn() + .mockImplementation(async (): Promise => getRulesSchemaMock()); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_custom_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_custom_rule.ts deleted file mode 100644 index 963cac7e10dd1..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_custom_rule.ts +++ /dev/null @@ -1,44 +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 type { RulesClient } from '@kbn/alerting-plugin/server'; -import { stringifyZodError } from '@kbn/zod-helpers'; -import type { CreateCustomRuleArgs } from '../detection_rules_client_interface'; -import type { MlAuthz } from '../../../../../machine_learning/authz'; -import type { RuleParams } from '../../../../rule_schema'; -import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; -import { - convertCreateAPIToInternalSchema, - internalRuleToAPIResponse, -} from '../../../normalization/rule_converters'; -import { validateMlAuth, RuleResponseValidationError } from '../utils'; - -export const createCustomRule = async ( - rulesClient: RulesClient, - args: CreateCustomRuleArgs, - mlAuthz: MlAuthz -): Promise => { - const { params } = args; - await validateMlAuth(mlAuthz, params.type); - - const internalRule = convertCreateAPIToInternalSchema(params, { immutable: false }); - const rule = await rulesClient.create({ - data: internalRule, - }); - - /* Trying to convert the rule to a RuleResponse object */ - const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(rule)); - - if (!parseResult.success) { - throw new RuleResponseValidationError({ - message: stringifyZodError(parseResult.error), - ruleId: rule.params.ruleId, - }); - } - - return parseResult.data; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_prebuilt_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_prebuilt_rule.ts deleted file mode 100644 index 0f0a4aea12d7b..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_prebuilt_rule.ts +++ /dev/null @@ -1,49 +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 type { RulesClient } from '@kbn/alerting-plugin/server'; -import { stringifyZodError } from '@kbn/zod-helpers'; -import type { CreatePrebuiltRuleArgs } from '../detection_rules_client_interface'; -import type { MlAuthz } from '../../../../../machine_learning/authz'; -import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; -import type { RuleParams } from '../../../../rule_schema'; -import { - convertCreateAPIToInternalSchema, - internalRuleToAPIResponse, -} from '../../../normalization/rule_converters'; -import { validateMlAuth, RuleResponseValidationError } from '../utils'; - -export const createPrebuiltRule = async ( - rulesClient: RulesClient, - args: CreatePrebuiltRuleArgs, - mlAuthz: MlAuthz -): Promise => { - const { params } = args; - - await validateMlAuth(mlAuthz, params.type); - - const internalRule = convertCreateAPIToInternalSchema(params, { - immutable: true, - defaultEnabled: false, - }); - - const rule = await rulesClient.create({ - data: internalRule, - }); - - /* Trying to convert the rule to a RuleResponse object */ - const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(rule)); - - if (!parseResult.success) { - throw new RuleResponseValidationError({ - message: stringifyZodError(parseResult.error), - ruleId: rule.params.ruleId, - }); - } - - return parseResult.data; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_rule.ts new file mode 100644 index 0000000000000..772e0c775d8b4 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/create_rule.ts @@ -0,0 +1,55 @@ +/* + * 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 type { RulesClient } from '@kbn/alerting-plugin/server'; +import { ruleTypeMappings } from '@kbn/securitysolution-rules'; +import { SERVER_APP_ID } from '../../../../../../../common'; +import type { + RuleCreateProps, + RuleResponse, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import type { MlAuthz } from '../../../../../machine_learning/authz'; +import type { RuleParams } from '../../../../rule_schema'; +import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response'; +import { convertRuleResponseToAlertingRule } from '../converters/convert_rule_response_to_alerting_rule'; +import { applyRuleDefaults } from '../mergers/apply_rule_defaults'; +import { validateMlAuth } from '../utils'; + +interface CreateRuleOptions { + rulesClient: RulesClient; + mlAuthz: MlAuthz; + rule: RuleCreateProps & { immutable: boolean }; + id?: string; + allowMissingConnectorSecrets?: boolean; +} + +export const createRule = async ({ + rulesClient, + mlAuthz, + rule, + id, + allowMissingConnectorSecrets, +}: CreateRuleOptions): Promise => { + await validateMlAuth(mlAuthz, rule.type); + + const ruleWithDefaults = applyRuleDefaults(rule); + + const payload = { + ...convertRuleResponseToAlertingRule(ruleWithDefaults), + alertTypeId: ruleTypeMappings[rule.type], + consumer: SERVER_APP_ID, + enabled: rule.enabled ?? false, + }; + + const createdRule = await rulesClient.create({ + data: payload, + options: { id }, + allowMissingConnectorSecrets, + }); + + return convertAlertingRuleToRuleResponse(createdRule); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/delete_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/delete_rule.ts index ec1491e8159d7..4a9ca8abcdeb1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/delete_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/delete_rule.ts @@ -6,9 +6,13 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; -import type { DeleteRuleArgs } from '../detection_rules_client_interface'; +import type { RuleObjectId } from '../../../../../../../common/api/detection_engine'; -export const deleteRule = async (rulesClient: RulesClient, args: DeleteRuleArgs): Promise => { - const { ruleId } = args; +interface DeleteRuleParams { + rulesClient: RulesClient; + ruleId: RuleObjectId; +} + +export const deleteRule = async ({ rulesClient, ruleId }: DeleteRuleParams): Promise => { await rulesClient.delete({ id: ruleId }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_id.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_id.ts new file mode 100644 index 0000000000000..39ca15fda42f3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_id.ts @@ -0,0 +1,34 @@ +/* + * 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 type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { + RuleObjectId, + RuleResponse, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import type { RuleParams } from '../../../../rule_schema'; +import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response'; + +interface GethRuleByIdOptions { + rulesClient: RulesClient; + id: RuleObjectId; +} + +export const getRuleById = async ({ + rulesClient, + id, +}: GethRuleByIdOptions): Promise => { + try { + const rule = await rulesClient.resolve({ id }); + return convertAlertingRuleToRuleResponse(rule); + } catch (err) { + if (err?.output?.statusCode === 404) { + return null; + } + throw err; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_id_or_rule_id.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_id_or_rule_id.ts new file mode 100644 index 0000000000000..fce28d1a1c030 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_id_or_rule_id.ts @@ -0,0 +1,36 @@ +/* + * 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 type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { + RuleObjectId, + RuleResponse, + RuleSignatureId, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { invariant } from '../../../../../../../common/utils/invariant'; +import { getRuleById } from './get_rule_by_id'; +import { getRuleByRuleId } from './get_rule_by_rule_id'; + +interface GetRuleByIdOptions { + rulesClient: RulesClient; + id: RuleObjectId | undefined; + ruleId: RuleSignatureId | undefined; +} + +export const getRuleByIdOrRuleId = async ({ + rulesClient, + id, + ruleId, +}: GetRuleByIdOptions): Promise => { + if (id != null) { + return getRuleById({ rulesClient, id }); + } + if (ruleId != null) { + return getRuleByRuleId({ rulesClient, ruleId }); + } + invariant(false, 'Either id or ruleId must be provided'); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_rule_id.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_rule_id.ts new file mode 100644 index 0000000000000..fda00cd292b88 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/get_rule_by_rule_id.ts @@ -0,0 +1,38 @@ +/* + * 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 type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { + RuleResponse, + RuleSignatureId, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { findRules } from '../../search/find_rules'; +import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response'; + +interface GetRuleByRuleIdOptions { + rulesClient: RulesClient; + ruleId: RuleSignatureId; +} + +export const getRuleByRuleId = async ({ + rulesClient, + ruleId, +}: GetRuleByRuleIdOptions): Promise => { + const findRuleResponse = await findRules({ + rulesClient, + filter: `alert.attributes.params.ruleId: "${ruleId}"`, + page: 1, + fields: undefined, + perPage: undefined, + sortField: undefined, + sortOrder: undefined, + }); + if (findRuleResponse.data.length === 0) { + return null; + } + return convertAlertingRuleToRuleResponse(findRuleResponse.data[0]); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts index 55a0399f1a528..adb28133b62f3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts @@ -6,78 +6,67 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; -import { stringifyZodError } from '@kbn/zod-helpers'; +import type { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; import type { MlAuthz } from '../../../../../machine_learning/authz'; -import type { ImportRuleArgs } from '../detection_rules_client_interface'; -import type { RuleAlertType, RuleParams } from '../../../../rule_schema'; +import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; import { createBulkErrorObject } from '../../../../routes/utils'; -import { - convertCreateAPIToInternalSchema, - convertUpdateAPIToInternalSchema, - internalRuleToAPIResponse, -} from '../../../normalization/rule_converters'; -import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; - -import { validateMlAuth, RuleResponseValidationError } from '../utils'; +import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response'; +import { convertRuleResponseToAlertingRule } from '../converters/convert_rule_response_to_alerting_rule'; +import type { ImportRuleArgs } from '../detection_rules_client_interface'; +import { applyRuleUpdate } from '../mergers/apply_rule_update'; +import { validateMlAuth } from '../utils'; +import { createRule } from './create_rule'; +import { getRuleByRuleId } from './get_rule_by_rule_id'; -import { readRules } from '../read_rules'; +interface ImportRuleOptions { + rulesClient: RulesClient; + prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; + importRulePayload: ImportRuleArgs; + mlAuthz: MlAuthz; +} -export const importRule = async ( - rulesClient: RulesClient, - importRulePayload: ImportRuleArgs, - mlAuthz: MlAuthz -): Promise => { +export const importRule = async ({ + rulesClient, + importRulePayload, + prebuiltRuleAssetClient, + mlAuthz, +}: ImportRuleOptions): Promise => { const { ruleToImport, overwriteRules, allowMissingConnectorSecrets } = importRulePayload; await validateMlAuth(mlAuthz, ruleToImport.type); - const existingRule = await readRules({ + const existingRule = await getRuleByRuleId({ rulesClient, ruleId: ruleToImport.rule_id, - id: undefined, }); if (existingRule && !overwriteRules) { throw createBulkErrorObject({ - ruleId: existingRule.params.ruleId, + ruleId: existingRule.rule_id, statusCode: 409, - message: `rule_id: "${existingRule.params.ruleId}" already exists`, + message: `rule_id: "${existingRule.rule_id}" already exists`, }); } - let importedInternalRule: RuleAlertType; - if (existingRule && overwriteRules) { - const ruleUpdateParams = convertUpdateAPIToInternalSchema({ + const ruleWithUpdates = await applyRuleUpdate({ + prebuiltRuleAssetClient, existingRule, ruleUpdate: ruleToImport, }); - importedInternalRule = await rulesClient.update({ + const updatedRule = await rulesClient.update({ id: existingRule.id, - data: ruleUpdateParams, - }); - } else { - /* Rule does not exist, so we'll create it */ - const ruleCreateParams = convertCreateAPIToInternalSchema(ruleToImport, { - immutable: false, - }); - - importedInternalRule = await rulesClient.create({ - data: ruleCreateParams, - allowMissingConnectorSecrets, + data: convertRuleResponseToAlertingRule(ruleWithUpdates), }); + return convertAlertingRuleToRuleResponse(updatedRule); } - /* Trying to convert an internal rule to a RuleResponse object */ - const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(importedInternalRule)); - - if (!parseResult.success) { - throw new RuleResponseValidationError({ - message: stringifyZodError(parseResult.error), - ruleId: importedInternalRule.params.ruleId, - }); - } - - return parseResult.data; + /* Rule does not exist, so we'll create it */ + return createRule({ + rulesClient, + mlAuthz, + rule: ruleToImport, + allowMissingConnectorSecrets, + }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts index ce9956c5eec84..d615d5fc5a817 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts @@ -6,34 +6,35 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; -import { stringifyZodError } from '@kbn/zod-helpers'; +import type { + RulePatchProps, + RuleResponse, +} from '../../../../../../../common/api/detection_engine/model/rule_schema'; import type { MlAuthz } from '../../../../../machine_learning/authz'; -import type { PatchRuleArgs } from '../detection_rules_client_interface'; +import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; +import { applyRulePatch } from '../mergers/apply_rule_patch'; import { getIdError } from '../../../utils/utils'; -import { - convertPatchAPIToInternalSchema, - internalRuleToAPIResponse, -} from '../../../normalization/rule_converters'; -import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response'; +import { convertRuleResponseToAlertingRule } from '../converters/convert_rule_response_to_alerting_rule'; +import { ClientError, toggleRuleEnabledOnUpdate, validateMlAuth } from '../utils'; +import { getRuleByIdOrRuleId } from './get_rule_by_id_or_rule_id'; -import { - validateMlAuth, - ClientError, - toggleRuleEnabledOnUpdate, - RuleResponseValidationError, -} from '../utils'; +interface PatchRuleOptions { + rulesClient: RulesClient; + prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; + rulePatch: RulePatchProps; + mlAuthz: MlAuthz; +} -import { readRules } from '../read_rules'; +export const patchRule = async ({ + rulesClient, + prebuiltRuleAssetClient, + rulePatch, + mlAuthz, +}: PatchRuleOptions): Promise => { + const { rule_id: ruleId, id } = rulePatch; -export const patchRule = async ( - rulesClient: RulesClient, - args: PatchRuleArgs, - mlAuthz: MlAuthz -): Promise => { - const { nextParams } = args; - const { rule_id: ruleId, id } = nextParams; - - const existingRule = await readRules({ + const existingRule = await getRuleByIdOrRuleId({ rulesClient, ruleId, id, @@ -44,32 +45,20 @@ export const patchRule = async ( throw new ClientError(error.message, error.statusCode); } - await validateMlAuth(mlAuthz, nextParams.type ?? existingRule.params.type); + await validateMlAuth(mlAuthz, rulePatch.type ?? existingRule.type); - const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule); + const patchedRule = await applyRulePatch({ + prebuiltRuleAssetClient, + existingRule, + rulePatch, + }); const patchedInternalRule = await rulesClient.update({ id: existingRule.id, - data: patchedRule, + data: convertRuleResponseToAlertingRule(patchedRule), }); - const { enabled } = await toggleRuleEnabledOnUpdate( - rulesClient, - existingRule, - nextParams.enabled - ); - - /* Trying to convert the internal rule to a RuleResponse object */ - const parseResult = RuleResponse.safeParse( - internalRuleToAPIResponse({ ...patchedInternalRule, enabled }) - ); - - if (!parseResult.success) { - throw new RuleResponseValidationError({ - message: stringifyZodError(parseResult.error), - ruleId: patchedInternalRule.params.ruleId, - }); - } + const { enabled } = await toggleRuleEnabledOnUpdate(rulesClient, existingRule, patchedRule); - return parseResult.data; + return convertAlertingRuleToRuleResponse({ ...patchedInternalRule, enabled }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts index 8684a7ccd2c61..cf42074c2a042 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts @@ -6,36 +6,37 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; -import { stringifyZodError } from '@kbn/zod-helpers'; +import type { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; import type { MlAuthz } from '../../../../../machine_learning/authz'; -import type { UpdateRuleArgs } from '../detection_rules_client_interface'; +import { applyRuleUpdate } from '../mergers/apply_rule_update'; import { getIdError } from '../../../utils/utils'; -import { - convertUpdateAPIToInternalSchema, - internalRuleToAPIResponse, -} from '../../../normalization/rule_converters'; -import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; +import { convertRuleResponseToAlertingRule } from '../converters/convert_rule_response_to_alerting_rule'; -import { - validateMlAuth, - ClientError, - toggleRuleEnabledOnUpdate, - RuleResponseValidationError, -} from '../utils'; +import { ClientError, toggleRuleEnabledOnUpdate, validateMlAuth } from '../utils'; -import { readRules } from '../read_rules'; +import type { RuleUpdateProps } from '../../../../../../../common/api/detection_engine'; +import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; +import { getRuleByIdOrRuleId } from './get_rule_by_id_or_rule_id'; +import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response'; -export const updateRule = async ( - rulesClient: RulesClient, - args: UpdateRuleArgs, - mlAuthz: MlAuthz -): Promise => { - const { ruleUpdate } = args; +interface UpdateRuleArguments { + rulesClient: RulesClient; + prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; + ruleUpdate: RuleUpdateProps; + mlAuthz: MlAuthz; +} + +export const updateRule = async ({ + rulesClient, + prebuiltRuleAssetClient, + ruleUpdate, + mlAuthz, +}: UpdateRuleArguments): Promise => { const { rule_id: ruleId, id } = ruleUpdate; await validateMlAuth(mlAuthz, ruleUpdate.type); - const existingRule = await readRules({ + const existingRule = await getRuleByIdOrRuleId({ rulesClient, ruleId, id, @@ -46,33 +47,21 @@ export const updateRule = async ( throw new ClientError(error.message, error.statusCode); } - const newInternalRule = convertUpdateAPIToInternalSchema({ + const ruleWithUpdates = await applyRuleUpdate({ + prebuiltRuleAssetClient, existingRule, ruleUpdate, }); - const updatedInternalRule = await rulesClient.update({ + const updatedRule = await rulesClient.update({ id: existingRule.id, - data: newInternalRule, + data: convertRuleResponseToAlertingRule(ruleWithUpdates), }); - const { enabled } = await toggleRuleEnabledOnUpdate( - rulesClient, - existingRule, - ruleUpdate.enabled - ); - - /* Trying to convert the internal rule to a RuleResponse object */ - const parseResult = RuleResponse.safeParse( - internalRuleToAPIResponse({ ...updatedInternalRule, enabled }) - ); + const { enabled } = await toggleRuleEnabledOnUpdate(rulesClient, existingRule, ruleWithUpdates); - if (!parseResult.success) { - throw new RuleResponseValidationError({ - message: stringifyZodError(parseResult.error), - ruleId: updatedInternalRule.params.ruleId, - }); - } - - return parseResult.data; + return convertAlertingRuleToRuleResponse({ + ...updatedRule, + enabled, + }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts index 8c1079f5716db..4eef323be2fd9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts @@ -6,94 +6,74 @@ */ import type { RulesClient } from '@kbn/alerting-plugin/server'; -import { stringifyZodError } from '@kbn/zod-helpers'; +import type { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; import type { MlAuthz } from '../../../../../machine_learning/authz'; -import type { RuleParams } from '../../../../rule_schema'; -import type { UpgradePrebuiltRuleArgs } from '../detection_rules_client_interface'; -import { - convertPatchAPIToInternalSchema, - convertCreateAPIToInternalSchema, - internalRuleToAPIResponse, -} from '../../../normalization/rule_converters'; -import { transformAlertToRuleAction } from '../../../../../../../common/detection_engine/transform_actions'; -import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema'; - -import { validateMlAuth, ClientError, RuleResponseValidationError } from '../utils'; - -import { readRules } from '../read_rules'; - -export const upgradePrebuiltRule = async ( - rulesClient: RulesClient, - upgradePrebuiltRulePayload: UpgradePrebuiltRuleArgs, - mlAuthz: MlAuthz -): Promise => { - const { ruleAsset } = upgradePrebuiltRulePayload; - +import type { PrebuiltRuleAsset } from '../../../../prebuilt_rules'; +import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; +import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response'; +import { convertRuleResponseToAlertingRule } from '../converters/convert_rule_response_to_alerting_rule'; +import { applyRulePatch } from '../mergers/apply_rule_patch'; +import { ClientError, validateMlAuth } from '../utils'; +import { createRule } from './create_rule'; +import { getRuleByRuleId } from './get_rule_by_rule_id'; + +export const upgradePrebuiltRule = async ({ + rulesClient, + ruleAsset, + mlAuthz, + prebuiltRuleAssetClient, +}: { + rulesClient: RulesClient; + ruleAsset: PrebuiltRuleAsset; + mlAuthz: MlAuthz; + prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; +}): Promise => { await validateMlAuth(mlAuthz, ruleAsset.type); - const existingRule = await readRules({ + const existingRule = await getRuleByRuleId({ rulesClient, ruleId: ruleAsset.rule_id, - id: undefined, }); if (!existingRule) { throw new ClientError(`Failed to find rule ${ruleAsset.rule_id}`, 500); } - if (ruleAsset.type !== existingRule.params.type) { + if (ruleAsset.type !== existingRule.type) { // If we're trying to change the type of a prepackaged rule, we need to delete the old one // and replace it with the new rule, keeping the enabled setting, actions, throttle, id, // and exception lists from the old rule await rulesClient.delete({ id: existingRule.id }); - const internalRule = convertCreateAPIToInternalSchema( - { + const createdRule = await createRule({ + rulesClient, + mlAuthz, + rule: { ...ruleAsset, + immutable: true, enabled: existingRule.enabled, - exceptions_list: existingRule.params.exceptionsList, - actions: existingRule.actions.map(transformAlertToRuleAction), - timeline_id: existingRule.params.timelineId, - timeline_title: existingRule.params.timelineTitle, + exceptions_list: existingRule.exceptions_list, + actions: existingRule.actions, + timeline_id: existingRule.timeline_id, + timeline_title: existingRule.timeline_title, }, - { immutable: true, defaultEnabled: existingRule.enabled } - ); - - const createdRule = await rulesClient.create({ - data: internalRule, - options: { id: existingRule.id }, + id: existingRule.id, }); - /* Trying to convert the rule to a RuleResponse object */ - const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(createdRule)); - - if (!parseResult.success) { - throw new RuleResponseValidationError({ - message: stringifyZodError(parseResult.error), - ruleId: createdRule.params.ruleId, - }); - } - - return parseResult.data; + return createdRule; } // Else, simply patch it. - const patchedRule = convertPatchAPIToInternalSchema(ruleAsset, existingRule); + const patchedRule = await applyRulePatch({ + prebuiltRuleAssetClient, + existingRule, + rulePatch: ruleAsset, + }); const patchedInternalRule = await rulesClient.update({ id: existingRule.id, - data: patchedRule, + data: convertRuleResponseToAlertingRule(patchedRule), }); - /* Trying to convert the internal rule to a RuleResponse object */ - const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(patchedInternalRule)); - - if (!parseResult.success) { - throw new RuleResponseValidationError({ - message: stringifyZodError(parseResult.error), - ruleId: patchedInternalRule.params.ruleId, - }); - } - - return parseResult.data; + return convertAlertingRuleToRuleResponse(patchedInternalRule); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/read_rules.ts index d699d5ee7dd55..67c7746bd0eb3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/read_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/read_rules.ts @@ -30,6 +30,8 @@ export interface ReadRuleOptions { * be returned as a not-found or a thrown error that is not 404. * @param ruleId - This is a close second to being fast as long as it can find the rule_id from * a filter query against the ruleId property in params using `alert.attributes.params.ruleId: "${ruleId}"` + * + * @deprecated Should be replaced with DetectionRulesClient.getRuleById once it's implemented */ export const readRules = async ({ rulesClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/utils.ts index 4f25497b30564..db2af377eb5b1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/utils.ts @@ -12,21 +12,21 @@ import type { RulesClient } from '@kbn/alerting-plugin/server'; import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import type { MlAuthz } from '../../../../machine_learning/authz'; -import type { RuleAlertType } from '../../../rule_schema'; import type { RuleSignatureId } from '../../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; import { throwAuthzError } from '../../../../machine_learning/validation'; +import type { RuleResponse } from '../../../../../../common/api/detection_engine'; export const toggleRuleEnabledOnUpdate = async ( rulesClient: RulesClient, - existingRule: RuleAlertType, - updatedRuleEnabled?: boolean + existingRule: RuleResponse, + updatedRule: RuleResponse ): Promise<{ enabled: boolean }> => { - if (existingRule.enabled && updatedRuleEnabled === false) { + if (existingRule.enabled && !updatedRule.enabled) { await rulesClient.disable({ id: existingRule.id }); return { enabled: false }; } - if (!existingRule.enabled && updatedRuleEnabled === true) { + if (!existingRule.enabled && updatedRule.enabled) { await rulesClient.enable({ id: existingRule.id }); return { enabled: true }; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index 08794143ce161..5093393d6d657 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -15,7 +15,7 @@ import { getRuleMock, } from '../../../routes/__mocks__/request_responses'; import { getThreatMock } from '../../../../../../common/detection_engine/schemas/types/threat.mock'; -import { internalRuleToAPIResponse } from '../../normalization/rule_converters'; +import { internalRuleToAPIResponse } from '../detection_rules_client/converters/internal_rule_to_api_response'; import { getEqlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; import { getExportByObjectIds } from './get_export_by_object_ids'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts index ce57b33227ca4..7c3142aed85f6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.ts @@ -13,7 +13,7 @@ import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import type { RulesClient, PartialRule } from '@kbn/alerting-plugin/server'; import type { ActionsClient } from '@kbn/actions-plugin/server'; import { withSecuritySpan } from '../../../../../utils/with_security_span'; -import { internalRuleToAPIResponse } from '../../normalization/rule_converters'; +import { internalRuleToAPIResponse } from '../detection_rules_client/converters/internal_rule_to_api_response'; import type { RuleParams } from '../../../rule_schema'; import { hasValidRuleType } from '../../../rule_schema'; import { findRules } from '../search/find_rules'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.test.ts deleted file mode 100644 index 5df02371befa2..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.test.ts +++ /dev/null @@ -1,491 +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 { - commonParamsCamelToSnake, - patchTypeSpecificSnakeToCamel, - typeSpecificCamelToSnake, -} from './rule_converters'; -import { - getBaseRuleParams, - getEqlRuleParams, - getEsqlRuleParams, - getMlRuleParams, - getNewTermsRuleParams, - getQueryRuleParams, - getSavedQueryRuleParams, - getThreatRuleParams, - getThresholdRuleParams, -} from '../../rule_schema/mocks'; -import type { - AlertSuppressionDuration, - PatchRuleRequestBody, - AlertSuppressionMissingFieldsStrategy, -} from '../../../../../common/api/detection_engine'; - -describe('rule_converters', () => { - describe('patchTypeSpecificSnakeToCamel', () => { - describe('EQL', () => { - test('should accept EQL params when existing rule type is EQL', () => { - const patchParams = { - timestamp_field: 'event.created', - event_category_override: 'event.not_category', - tiebreaker_field: 'event.created', - }; - const rule = getEqlRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - timestampField: 'event.created', - eventCategoryOverride: 'event.not_category', - tiebreakerField: 'event.created', - }) - ); - }); - test('should accept EQL params with suppression in snake case and convert to camel case when rule type is EQL', () => { - const patchParams = { - timestamp_field: 'event.created', - event_category_override: 'event.not_category', - tiebreaker_field: 'event.created', - alert_suppression: { - group_by: ['event.type'], - duration: { - value: 10, - unit: 'm', - } as AlertSuppressionDuration, - missing_fields_strategy: 'suppress', - }, - }; - const rule = getEqlRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - timestampField: 'event.created', - eventCategoryOverride: 'event.not_category', - tiebreakerField: 'event.created', - alertSuppression: { - groupBy: ['event.type'], - duration: { - value: 10, - unit: 'm', - }, - missingFieldsStrategy: 'suppress', - }, - }) - ); - }); - test('should reject invalid EQL params when existing rule type is EQL', () => { - const patchParams = { - timestamp_field: 1, - event_category_override: 1, - tiebreaker_field: 1, - } as PatchRuleRequestBody; - const rule = getEqlRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - 'event_category_override: Expected string, received number, tiebreaker_field: Expected string, received number, timestamp_field: Expected string, received number' - ); - }); - test('should reject EQL params with invalid suppression group_by field', () => { - const patchParams = { - timestamp_field: 'event.created', - event_category_override: 'event.not_category', - tiebreaker_field: 'event.created', - alert_suppression: { - group_by: 'event.type', - duration: { - value: 10, - unit: 'm', - } as AlertSuppressionDuration, - missing_fields_strategy: 'suppress', - }, - }; - const rule = getEqlRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - 'alert_suppression.group_by: Expected array, received string' - ); - }); - }); - - describe('machine learning rules', () => { - test('should accept machine learning params when existing rule type is machine learning', () => { - const patchParams = { - anomaly_threshold: 5, - }; - const rule = getMlRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - anomalyThreshold: 5, - }) - ); - }); - - test('should reject invalid machine learning params when existing rule type is machine learning', () => { - const patchParams = { - anomaly_threshold: 'invalid', - } as PatchRuleRequestBody; - const rule = getMlRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - 'anomaly_threshold: Expected number, received string' - ); - }); - - it('accepts suppression params', () => { - const patchParams = { - alert_suppression: { - group_by: ['agent.name'], - missing_fields_strategy: 'suppress' as const, - }, - }; - const rule = getMlRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - - expect(patchedParams).toEqual( - expect.objectContaining({ - alertSuppression: { - groupBy: ['agent.name'], - missingFieldsStrategy: 'suppress', - }, - }) - ); - }); - }); - - test('should accept threat match params when existing rule type is threat match', () => { - const patchParams = { - threat_indicator_path: 'my.indicator', - threat_query: 'test-query', - }; - const rule = getThreatRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - threatIndicatorPath: 'my.indicator', - threatQuery: 'test-query', - }) - ); - }); - - test('should reject invalid threat match params when existing rule type is threat match', () => { - const patchParams = { - threat_indicator_path: 1, - threat_query: 1, - } as PatchRuleRequestBody; - const rule = getThreatRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - 'threat_query: Expected string, received number, threat_indicator_path: Expected string, received number' - ); - }); - - test('should accept query params when existing rule type is query', () => { - const patchParams = { - index: ['new-test-index'], - language: 'lucene', - } as PatchRuleRequestBody; - const rule = getQueryRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - index: ['new-test-index'], - language: 'lucene', - }) - ); - }); - - test('should reject invalid query params when existing rule type is query', () => { - const patchParams = { - index: [1], - language: 'non-language', - } as PatchRuleRequestBody; - const rule = getQueryRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - "index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'" - ); - }); - - test('should accept saved query params when existing rule type is saved query', () => { - const patchParams = { - index: ['new-test-index'], - language: 'lucene', - } as PatchRuleRequestBody; - const rule = getSavedQueryRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - index: ['new-test-index'], - language: 'lucene', - }) - ); - }); - - test('should reject invalid saved query params when existing rule type is saved query', () => { - const patchParams = { - index: [1], - language: 'non-language', - } as PatchRuleRequestBody; - const rule = getSavedQueryRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - "index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'" - ); - }); - - test('should accept threshold params when existing rule type is threshold', () => { - const patchParams = { - threshold: { - field: ['host.name'], - value: 107, - }, - }; - const rule = getThresholdRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - threshold: { - field: ['host.name'], - value: 107, - }, - }) - ); - }); - - test('should reject invalid threshold params when existing rule type is threshold', () => { - const patchParams = { - threshold: { - field: ['host.name'], - value: 'invalid', - }, - } as PatchRuleRequestBody; - const rule = getThresholdRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - 'threshold.value: Expected number, received string' - ); - }); - - test('should accept ES|QL alerts suppression params', () => { - const patchParams = { - alert_suppression: { - group_by: ['agent.name'], - duration: { value: 4, unit: 'h' as const }, - missing_fields_strategy: 'doNotSuppress' as const, - }, - }; - const rule = getEsqlRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - alertSuppression: { - groupBy: ['agent.name'], - missingFieldsStrategy: 'doNotSuppress', - duration: { value: 4, unit: 'h' }, - }, - }) - ); - }); - - test('should accept threshold alerts suppression params', () => { - const patchParams = { - alert_suppression: { - duration: { value: 4, unit: 'h' as const }, - }, - }; - const rule = getThresholdRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - alertSuppression: { - duration: { value: 4, unit: 'h' }, - }, - }) - ); - }); - - test('should accept threat_match alerts suppression params', () => { - const patchParams = { - alert_suppression: { - group_by: ['agent.name'], - missing_fields_strategy: 'suppress' as const, - }, - }; - const rule = getThreatRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - alertSuppression: { - groupBy: ['agent.name'], - missingFieldsStrategy: 'suppress', - }, - }) - ); - }); - - test('should accept new_terms alerts suppression params', () => { - const patchParams = { - alert_suppression: { - group_by: ['agent.name'], - duration: { value: 4, unit: 'h' as const }, - missing_fields_strategy: 'suppress' as const, - }, - }; - const rule = getNewTermsRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - alertSuppression: { - groupBy: ['agent.name'], - missingFieldsStrategy: 'suppress', - duration: { value: 4, unit: 'h' }, - }, - }) - ); - }); - - test('should accept new terms params when existing rule type is new terms', () => { - const patchParams = { - new_terms_fields: ['event.new_field'], - }; - const rule = getNewTermsRuleParams(); - const patchedParams = patchTypeSpecificSnakeToCamel(patchParams, rule); - expect(patchedParams).toEqual( - expect.objectContaining({ - newTermsFields: ['event.new_field'], - }) - ); - }); - - test('should reject invalid new terms params when existing rule type is new terms', () => { - const patchParams = { - new_terms_fields: 'invalid', - } as PatchRuleRequestBody; - const rule = getNewTermsRuleParams(); - expect(() => patchTypeSpecificSnakeToCamel(patchParams, rule)).toThrowError( - 'new_terms_fields: Expected array, received string' - ); - }); - }); - - describe('typeSpecificCamelToSnake', () => { - describe('EQL', () => { - test('should accept EQL params when existing rule type is EQL', () => { - const params = { - timestampField: 'event.created', - eventCategoryOverride: 'event.not_category', - tiebreakerField: 'event.created', - }; - const eqlRule = { ...getEqlRuleParams(), ...params }; - const transformedParams = typeSpecificCamelToSnake(eqlRule); - expect(transformedParams).toEqual( - expect.objectContaining({ - timestamp_field: 'event.created', - event_category_override: 'event.not_category', - tiebreaker_field: 'event.created', - }) - ); - }); - - test('should accept EQL params with suppression in camel case and convert to snake case when rule type is EQL', () => { - const params = { - timestampField: 'event.created', - eventCategoryOverride: 'event.not_category', - tiebreakerField: 'event.created', - alertSuppression: { - groupBy: ['event.type'], - duration: { - value: 10, - unit: 'm', - } as AlertSuppressionDuration, - missingFieldsStrategy: 'suppress' as AlertSuppressionMissingFieldsStrategy, - }, - }; - const eqlRule = { ...getEqlRuleParams(), ...params }; - const transformedParams = typeSpecificCamelToSnake(eqlRule); - expect(transformedParams).toEqual( - expect.objectContaining({ - timestamp_field: 'event.created', - event_category_override: 'event.not_category', - tiebreaker_field: 'event.created', - alert_suppression: { - group_by: ['event.type'], - duration: { - value: 10, - unit: 'm', - } as AlertSuppressionDuration, - missing_fields_strategy: 'suppress', - }, - }) - ); - }); - }); - - describe('machine learning rules', () => { - it('accepts normal params', () => { - const params = { - anomalyThreshold: 74, - machineLearningJobId: ['job-1'], - }; - const ruleParams = { ...getMlRuleParams(), ...params }; - const transformedParams = typeSpecificCamelToSnake(ruleParams); - expect(transformedParams).toEqual( - expect.objectContaining({ - anomaly_threshold: 74, - machine_learning_job_id: ['job-1'], - }) - ); - }); - - it('accepts suppression params', () => { - const params = { - anomalyThreshold: 74, - machineLearningJobId: ['job-1'], - alertSuppression: { - groupBy: ['event.type'], - duration: { - value: 10, - unit: 'm', - } as AlertSuppressionDuration, - missingFieldsStrategy: 'suppress' as AlertSuppressionMissingFieldsStrategy, - }, - }; - const ruleParams = { ...getMlRuleParams(), ...params }; - const transformedParams = typeSpecificCamelToSnake(ruleParams); - expect(transformedParams).toEqual( - expect.objectContaining({ - anomaly_threshold: 74, - machine_learning_job_id: ['job-1'], - alert_suppression: { - group_by: ['event.type'], - duration: { - value: 10, - unit: 'm', - }, - missing_fields_strategy: 'suppress', - }, - }) - ); - }); - }); - }); - - describe('commonParamsCamelToSnake', () => { - test('should convert rule_source params to snake case', () => { - const transformedParams = commonParamsCamelToSnake({ - ...getBaseRuleParams(), - ruleSource: { - type: 'external', - isCustomized: false, - }, - }); - expect(transformedParams).toEqual( - expect.objectContaining({ - rule_source: { - type: 'external', - is_customized: false, - }, - }) - ); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts deleted file mode 100644 index db815f32fb5ed..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ /dev/null @@ -1,869 +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 { v4 as uuidv4 } from 'uuid'; - -import { stringifyZodError } from '@kbn/zod-helpers'; -import { BadRequestError } from '@kbn/securitysolution-es-utils'; -import { ruleTypeMappings } from '@kbn/securitysolution-rules'; -import type { ResolvedSanitizedRule, SanitizedRule } from '@kbn/alerting-plugin/common'; - -import type { RequiredOptional } from '@kbn/zod-helpers'; -import { - DEFAULT_INDICATOR_SOURCE_PATH, - DEFAULT_MAX_SIGNALS, - SERVER_APP_ID, -} from '../../../../../common/constants'; - -import type { PatchRuleRequestBody } from '../../../../../common/api/detection_engine/rule_management'; -import type { - RuleCreateProps, - RuleUpdateProps, - TypeSpecificCreateProps, - TypeSpecificResponse, -} from '../../../../../common/api/detection_engine/model/rule_schema'; -import { - EqlRulePatchFields, - EsqlRulePatchFields, - MachineLearningRulePatchFields, - NewTermsRulePatchFields, - QueryRulePatchFields, - SavedQueryRulePatchFields, - ThreatMatchRulePatchFields, - ThresholdRulePatchFields, - RuleResponse, -} from '../../../../../common/api/detection_engine/model/rule_schema'; - -import { - transformAlertToRuleAction, - transformAlertToRuleResponseAction, - transformRuleToAlertAction, - transformRuleToAlertResponseAction, -} from '../../../../../common/detection_engine/transform_actions'; - -import { - normalizeMachineLearningJobIds, - normalizeThresholdObject, -} from '../../../../../common/detection_engine/utils'; - -import { assertUnreachable } from '../../../../../common/utility_types'; - -import type { - InternalRuleCreate, - RuleParams, - TypeSpecificRuleParams, - BaseRuleParams, - EqlRuleParams, - EqlSpecificRuleParams, - EsqlRuleParams, - EsqlSpecificRuleParams, - ThreatRuleParams, - ThreatSpecificRuleParams, - QueryRuleParams, - QuerySpecificRuleParams, - SavedQuerySpecificRuleParams, - SavedQueryRuleParams, - ThresholdRuleParams, - ThresholdSpecificRuleParams, - MachineLearningRuleParams, - MachineLearningSpecificRuleParams, - InternalRuleUpdate, - NewTermsRuleParams, - NewTermsSpecificRuleParams, - RuleSourceCamelCased, -} from '../../rule_schema'; -import { transformFromAlertThrottle, transformToActionFrequency } from './rule_actions'; -import { - addEcsToRequiredFields, - convertAlertSuppressionToCamel, - convertAlertSuppressionToSnake, - migrateLegacyInvestigationFields, -} from '../utils/utils'; -import { createRuleExecutionSummary } from '../../rule_monitoring'; -import type { PrebuiltRuleAsset } from '../../prebuilt_rules'; -import { convertObjectKeysToSnakeCase } from '../../../../utils/object_case_converters'; - -const DEFAULT_FROM = 'now-6m' as const; -const DEFAULT_TO = 'now' as const; -const DEFAULT_INTERVAL = '5m' as const; - -// These functions provide conversions from the request API schema to the internal rule schema and from the internal rule schema -// to the response API schema. This provides static type-check assurances that the internal schema is in sync with the API schema for -// required and default-able fields. However, it is still possible to add an optional field to the API schema -// without causing a type-check error here. - -// Converts params from the snake case API format to the internal camel case format AND applies default values where needed. -// Notice that params.language is possibly undefined for most rule types in the API but we default it to kuery to match -// the legacy API behavior -export const typeSpecificSnakeToCamel = ( - params: TypeSpecificCreateProps -): TypeSpecificRuleParams => { - switch (params.type) { - case 'eql': { - return { - type: params.type, - language: params.language, - index: params.index, - dataViewId: params.data_view_id, - query: params.query, - filters: params.filters, - timestampField: params.timestamp_field, - eventCategoryOverride: params.event_category_override, - tiebreakerField: params.tiebreaker_field, - alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), - }; - } - case 'esql': { - return { - type: params.type, - language: params.language, - query: params.query, - alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), - }; - } - case 'threat_match': { - return { - type: params.type, - language: params.language ?? 'kuery', - index: params.index, - dataViewId: params.data_view_id, - query: params.query, - filters: params.filters, - savedId: params.saved_id, - threatFilters: params.threat_filters, - threatQuery: params.threat_query, - threatMapping: params.threat_mapping, - threatLanguage: params.threat_language, - threatIndex: params.threat_index, - threatIndicatorPath: params.threat_indicator_path ?? DEFAULT_INDICATOR_SOURCE_PATH, - concurrentSearches: params.concurrent_searches, - itemsPerSearch: params.items_per_search, - alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), - }; - } - case 'query': { - return { - type: params.type, - language: params.language ?? 'kuery', - index: params.index, - dataViewId: params.data_view_id, - query: params.query ?? '', - filters: params.filters, - savedId: params.saved_id, - responseActions: params.response_actions?.map(transformRuleToAlertResponseAction), - alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), - }; - } - case 'saved_query': { - return { - type: params.type, - language: params.language ?? 'kuery', - index: params.index, - query: params.query, - filters: params.filters, - savedId: params.saved_id, - dataViewId: params.data_view_id, - responseActions: params.response_actions?.map(transformRuleToAlertResponseAction), - alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), - }; - } - case 'threshold': { - return { - type: params.type, - language: params.language ?? 'kuery', - index: params.index, - dataViewId: params.data_view_id, - query: params.query, - filters: params.filters, - savedId: params.saved_id, - threshold: normalizeThresholdObject(params.threshold), - alertSuppression: params.alert_suppression?.duration - ? { duration: params.alert_suppression.duration } - : undefined, - }; - } - case 'machine_learning': { - return { - type: params.type, - anomalyThreshold: params.anomaly_threshold, - machineLearningJobId: normalizeMachineLearningJobIds(params.machine_learning_job_id), - alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), - }; - } - case 'new_terms': { - return { - type: params.type, - query: params.query, - newTermsFields: params.new_terms_fields, - historyWindowStart: params.history_window_start, - index: params.index, - filters: params.filters, - language: params.language ?? 'kuery', - dataViewId: params.data_view_id, - alertSuppression: convertAlertSuppressionToCamel(params.alert_suppression), - }; - } - default: { - return assertUnreachable(params); - } - } -}; - -const patchEqlParams = ( - params: EqlRulePatchFields, - existingRule: EqlRuleParams -): EqlSpecificRuleParams => { - return { - type: existingRule.type, - language: params.language ?? existingRule.language, - index: params.index ?? existingRule.index, - dataViewId: params.data_view_id ?? existingRule.dataViewId, - query: params.query ?? existingRule.query, - filters: params.filters ?? existingRule.filters, - timestampField: params.timestamp_field ?? existingRule.timestampField, - eventCategoryOverride: params.event_category_override ?? existingRule.eventCategoryOverride, - tiebreakerField: params.tiebreaker_field ?? existingRule.tiebreakerField, - alertSuppression: - convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, - }; -}; - -const patchEsqlParams = ( - params: EsqlRulePatchFields, - existingRule: EsqlRuleParams -): EsqlSpecificRuleParams => { - return { - type: existingRule.type, - language: params.language ?? existingRule.language, - query: params.query ?? existingRule.query, - alertSuppression: - convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, - }; -}; - -const patchThreatMatchParams = ( - params: ThreatMatchRulePatchFields, - existingRule: ThreatRuleParams -): ThreatSpecificRuleParams => { - return { - type: existingRule.type, - language: params.language ?? existingRule.language, - index: params.index ?? existingRule.index, - dataViewId: params.data_view_id ?? existingRule.dataViewId, - query: params.query ?? existingRule.query, - filters: params.filters ?? existingRule.filters, - savedId: params.saved_id ?? existingRule.savedId, - threatFilters: params.threat_filters ?? existingRule.threatFilters, - threatQuery: params.threat_query ?? existingRule.threatQuery, - threatMapping: params.threat_mapping ?? existingRule.threatMapping, - threatLanguage: params.threat_language ?? existingRule.threatLanguage, - threatIndex: params.threat_index ?? existingRule.threatIndex, - threatIndicatorPath: params.threat_indicator_path ?? existingRule.threatIndicatorPath, - concurrentSearches: params.concurrent_searches ?? existingRule.concurrentSearches, - itemsPerSearch: params.items_per_search ?? existingRule.itemsPerSearch, - alertSuppression: - convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, - }; -}; - -const patchQueryParams = ( - params: QueryRulePatchFields, - existingRule: QueryRuleParams -): QuerySpecificRuleParams => { - return { - type: existingRule.type, - language: params.language ?? existingRule.language, - index: params.index ?? existingRule.index, - dataViewId: params.data_view_id ?? existingRule.dataViewId, - query: params.query ?? existingRule.query, - filters: params.filters ?? existingRule.filters, - savedId: params.saved_id ?? existingRule.savedId, - responseActions: - params.response_actions?.map(transformRuleToAlertResponseAction) ?? - existingRule.responseActions, - alertSuppression: - convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, - }; -}; - -const patchSavedQueryParams = ( - params: SavedQueryRulePatchFields, - existingRule: SavedQueryRuleParams -): SavedQuerySpecificRuleParams => { - return { - type: existingRule.type, - language: params.language ?? existingRule.language, - index: params.index ?? existingRule.index, - dataViewId: params.data_view_id ?? existingRule.dataViewId, - query: params.query ?? existingRule.query, - filters: params.filters ?? existingRule.filters, - savedId: params.saved_id ?? existingRule.savedId, - responseActions: - params.response_actions?.map(transformRuleToAlertResponseAction) ?? - existingRule.responseActions, - alertSuppression: - convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, - }; -}; - -const patchThresholdParams = ( - params: ThresholdRulePatchFields, - existingRule: ThresholdRuleParams -): ThresholdSpecificRuleParams => { - return { - type: existingRule.type, - language: params.language ?? existingRule.language, - index: params.index ?? existingRule.index, - dataViewId: params.data_view_id ?? existingRule.dataViewId, - query: params.query ?? existingRule.query, - filters: params.filters ?? existingRule.filters, - savedId: params.saved_id ?? existingRule.savedId, - threshold: params.threshold - ? normalizeThresholdObject(params.threshold) - : existingRule.threshold, - alertSuppression: params.alert_suppression ?? existingRule.alertSuppression, - }; -}; - -const patchMachineLearningParams = ( - params: MachineLearningRulePatchFields, - existingRule: MachineLearningRuleParams -): MachineLearningSpecificRuleParams => { - return { - type: existingRule.type, - anomalyThreshold: params.anomaly_threshold ?? existingRule.anomalyThreshold, - machineLearningJobId: params.machine_learning_job_id - ? normalizeMachineLearningJobIds(params.machine_learning_job_id) - : existingRule.machineLearningJobId, - alertSuppression: - convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, - }; -}; - -const patchNewTermsParams = ( - params: NewTermsRulePatchFields, - existingRule: NewTermsRuleParams -): NewTermsSpecificRuleParams => { - return { - type: existingRule.type, - language: params.language ?? existingRule.language, - index: params.index ?? existingRule.index, - dataViewId: params.data_view_id ?? existingRule.dataViewId, - query: params.query ?? existingRule.query, - filters: params.filters ?? existingRule.filters, - newTermsFields: params.new_terms_fields ?? existingRule.newTermsFields, - historyWindowStart: params.history_window_start ?? existingRule.historyWindowStart, - alertSuppression: - convertAlertSuppressionToCamel(params.alert_suppression) ?? existingRule.alertSuppression, - }; -}; - -export const patchTypeSpecificSnakeToCamel = ( - params: PatchRuleRequestBody, - existingRule: RuleParams -): TypeSpecificRuleParams => { - // Here we do the validation of patch params by rule type to ensure that the fields that are - // passed in to patch are of the correct type, e.g. `query` is a string. Since the combined patch schema - // is a union of types where everything is optional, it's hard to do the validation before we know the rule type - - // a patch request that defines `event_category_override` as a number would not be assignable to the EQL patch schema, - // but would be assignable to the other rule types since they don't specify `event_category_override`. - switch (existingRule.type) { - case 'eql': { - const result = EqlRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchEqlParams(result.data, existingRule); - } - case 'esql': { - const result = EsqlRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchEsqlParams(result.data, existingRule); - } - case 'threat_match': { - const result = ThreatMatchRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchThreatMatchParams(result.data, existingRule); - } - case 'query': { - const result = QueryRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchQueryParams(result.data, existingRule); - } - case 'saved_query': { - const result = SavedQueryRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchSavedQueryParams(result.data, existingRule); - } - case 'threshold': { - const result = ThresholdRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchThresholdParams(result.data, existingRule); - } - case 'machine_learning': { - const result = MachineLearningRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchMachineLearningParams(result.data, existingRule); - } - case 'new_terms': { - const result = NewTermsRulePatchFields.safeParse(params); - if (!result.success) { - throw new BadRequestError(stringifyZodError(result.error)); - } - return patchNewTermsParams(result.data, existingRule); - } - default: { - return assertUnreachable(existingRule); - } - } -}; - -interface ConvertUpdateAPIToInternalSchemaProps { - existingRule: SanitizedRule; - ruleUpdate: RuleUpdateProps; -} - -export const convertUpdateAPIToInternalSchema = ({ - existingRule, - ruleUpdate, -}: ConvertUpdateAPIToInternalSchemaProps) => { - const alertActions = - ruleUpdate.actions?.map((action) => transformRuleToAlertAction(action)) ?? []; - const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); - - const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); - - const newInternalRule: InternalRuleUpdate = { - name: ruleUpdate.name, - tags: ruleUpdate.tags ?? [], - params: { - author: ruleUpdate.author ?? [], - buildingBlockType: ruleUpdate.building_block_type, - description: ruleUpdate.description, - ruleId: existingRule.params.ruleId, - falsePositives: ruleUpdate.false_positives ?? [], - from: ruleUpdate.from ?? 'now-6m', - investigationFields: ruleUpdate.investigation_fields, - immutable: existingRule.params.immutable, - ruleSource: convertImmutableToRuleSource(existingRule.params.immutable), - license: ruleUpdate.license, - outputIndex: ruleUpdate.output_index ?? '', - timelineId: ruleUpdate.timeline_id, - timelineTitle: ruleUpdate.timeline_title, - meta: ruleUpdate.meta, - maxSignals: ruleUpdate.max_signals ?? DEFAULT_MAX_SIGNALS, - relatedIntegrations: ruleUpdate.related_integrations ?? [], - requiredFields: addEcsToRequiredFields(ruleUpdate.required_fields), - riskScore: ruleUpdate.risk_score, - riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], - ruleNameOverride: ruleUpdate.rule_name_override, - setup: ruleUpdate.setup, - severity: ruleUpdate.severity, - severityMapping: ruleUpdate.severity_mapping ?? [], - threat: ruleUpdate.threat ?? [], - timestampOverride: ruleUpdate.timestamp_override, - timestampOverrideFallbackDisabled: ruleUpdate.timestamp_override_fallback_disabled, - to: ruleUpdate.to ?? 'now', - references: ruleUpdate.references ?? [], - namespace: ruleUpdate.namespace, - note: ruleUpdate.note, - version: ruleUpdate.version ?? existingRule.params.version, - exceptionsList: ruleUpdate.exceptions_list ?? [], - ...typeSpecificParams, - }, - schedule: { interval: ruleUpdate.interval ?? '5m' }, - actions, - }; - - return newInternalRule; -}; - -// eslint-disable-next-line complexity -export const convertPatchAPIToInternalSchema = ( - nextParams: PatchRuleRequestBody, - existingRule: SanitizedRule -): InternalRuleUpdate => { - const typeSpecificParams = patchTypeSpecificSnakeToCamel(nextParams, existingRule.params); - const existingParams = existingRule.params; - - const alertActions = - nextParams.actions?.map((action) => transformRuleToAlertAction(action)) ?? existingRule.actions; - const throttle = nextParams.throttle ?? transformFromAlertThrottle(existingRule); - const actions = transformToActionFrequency(alertActions, throttle); - - return { - name: nextParams.name ?? existingRule.name, - tags: nextParams.tags ?? existingRule.tags, - params: { - author: nextParams.author ?? existingParams.author, - buildingBlockType: nextParams.building_block_type ?? existingParams.buildingBlockType, - description: nextParams.description ?? existingParams.description, - ruleId: existingParams.ruleId, - falsePositives: nextParams.false_positives ?? existingParams.falsePositives, - investigationFields: nextParams.investigation_fields ?? existingParams.investigationFields, - from: nextParams.from ?? existingParams.from, - immutable: existingParams.immutable, - ruleSource: convertImmutableToRuleSource(existingParams.immutable), - license: nextParams.license ?? existingParams.license, - outputIndex: nextParams.output_index ?? existingParams.outputIndex, - timelineId: nextParams.timeline_id ?? existingParams.timelineId, - timelineTitle: nextParams.timeline_title ?? existingParams.timelineTitle, - meta: nextParams.meta ?? existingParams.meta, - maxSignals: nextParams.max_signals ?? existingParams.maxSignals, - relatedIntegrations: nextParams.related_integrations ?? existingParams.relatedIntegrations, - requiredFields: addEcsToRequiredFields(nextParams.required_fields), - riskScore: nextParams.risk_score ?? existingParams.riskScore, - riskScoreMapping: nextParams.risk_score_mapping ?? existingParams.riskScoreMapping, - ruleNameOverride: nextParams.rule_name_override ?? existingParams.ruleNameOverride, - setup: nextParams.setup ?? existingParams.setup, - severity: nextParams.severity ?? existingParams.severity, - severityMapping: nextParams.severity_mapping ?? existingParams.severityMapping, - threat: nextParams.threat ?? existingParams.threat, - timestampOverride: nextParams.timestamp_override ?? existingParams.timestampOverride, - timestampOverrideFallbackDisabled: - nextParams.timestamp_override_fallback_disabled ?? - existingParams.timestampOverrideFallbackDisabled, - to: nextParams.to ?? existingParams.to, - references: nextParams.references ?? existingParams.references, - namespace: nextParams.namespace ?? existingParams.namespace, - note: nextParams.note ?? existingParams.note, - version: nextParams.version ?? existingParams.version, - exceptionsList: nextParams.exceptions_list ?? existingParams.exceptionsList, - ...typeSpecificParams, - }, - schedule: { interval: nextParams.interval ?? existingRule.schedule.interval }, - actions, - }; -}; - -interface RuleCreateOptions { - immutable?: boolean; - defaultEnabled?: boolean; -} - -// eslint-disable-next-line complexity -export const convertCreateAPIToInternalSchema = ( - input: RuleCreateProps, - options?: RuleCreateOptions -): InternalRuleCreate => { - const { immutable = false, defaultEnabled = true } = options ?? {}; - - const typeSpecificParams = typeSpecificSnakeToCamel(input); - const newRuleId = input.rule_id ?? uuidv4(); - - const alertActions = input.actions?.map((action) => transformRuleToAlertAction(action)) ?? []; - const actions = transformToActionFrequency(alertActions, input.throttle); - - return { - name: input.name, - tags: input.tags ?? [], - alertTypeId: ruleTypeMappings[input.type], - consumer: SERVER_APP_ID, - params: { - author: input.author ?? [], - buildingBlockType: input.building_block_type, - description: input.description, - ruleId: newRuleId, - falsePositives: input.false_positives ?? [], - investigationFields: input.investigation_fields, - from: input.from ?? DEFAULT_FROM, - immutable, - ruleSource: convertImmutableToRuleSource(immutable), - license: input.license, - outputIndex: input.output_index ?? '', - timelineId: input.timeline_id, - timelineTitle: input.timeline_title, - meta: input.meta, - maxSignals: input.max_signals ?? DEFAULT_MAX_SIGNALS, - riskScore: input.risk_score, - riskScoreMapping: input.risk_score_mapping ?? [], - ruleNameOverride: input.rule_name_override, - severity: input.severity, - severityMapping: input.severity_mapping ?? [], - threat: input.threat ?? [], - timestampOverride: input.timestamp_override, - timestampOverrideFallbackDisabled: input.timestamp_override_fallback_disabled, - to: input.to ?? DEFAULT_TO, - references: input.references ?? [], - namespace: input.namespace, - note: input.note, - version: input.version ?? 1, - exceptionsList: input.exceptions_list ?? [], - relatedIntegrations: input.related_integrations ?? [], - requiredFields: addEcsToRequiredFields(input.required_fields), - setup: input.setup ?? '', - ...typeSpecificParams, - }, - schedule: { interval: input.interval ?? '5m' }, - enabled: input.enabled ?? defaultEnabled, - actions, - }; -}; - -// Converts the internal rule data structure to the response API schema -export const typeSpecificCamelToSnake = ( - params: TypeSpecificRuleParams -): RequiredOptional => { - switch (params.type) { - case 'eql': { - return { - type: params.type, - language: params.language, - index: params.index, - data_view_id: params.dataViewId, - query: params.query, - filters: params.filters, - timestamp_field: params.timestampField, - event_category_override: params.eventCategoryOverride, - tiebreaker_field: params.tiebreakerField, - alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), - }; - } - case 'esql': { - return { - type: params.type, - language: params.language, - query: params.query, - alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), - }; - } - case 'threat_match': { - return { - type: params.type, - language: params.language, - index: params.index, - data_view_id: params.dataViewId, - query: params.query, - filters: params.filters, - saved_id: params.savedId, - threat_filters: params.threatFilters, - threat_query: params.threatQuery, - threat_mapping: params.threatMapping, - threat_language: params.threatLanguage, - threat_index: params.threatIndex, - threat_indicator_path: params.threatIndicatorPath, - concurrent_searches: params.concurrentSearches, - items_per_search: params.itemsPerSearch, - alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), - }; - } - case 'query': { - return { - type: params.type, - language: params.language, - index: params.index, - data_view_id: params.dataViewId, - query: params.query, - filters: params.filters, - saved_id: params.savedId, - response_actions: params.responseActions?.map(transformAlertToRuleResponseAction), - alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), - }; - } - case 'saved_query': { - return { - type: params.type, - language: params.language, - index: params.index, - query: params.query, - filters: params.filters, - saved_id: params.savedId, - data_view_id: params.dataViewId, - response_actions: params.responseActions?.map(transformAlertToRuleResponseAction), - alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), - }; - } - case 'threshold': { - return { - type: params.type, - language: params.language, - index: params.index, - data_view_id: params.dataViewId, - query: params.query, - filters: params.filters, - saved_id: params.savedId, - threshold: params.threshold, - alert_suppression: params.alertSuppression?.duration - ? { duration: params.alertSuppression?.duration } - : undefined, - }; - } - case 'machine_learning': { - return { - type: params.type, - anomaly_threshold: params.anomalyThreshold, - machine_learning_job_id: params.machineLearningJobId, - alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), - }; - } - case 'new_terms': { - return { - type: params.type, - query: params.query, - new_terms_fields: params.newTermsFields, - history_window_start: params.historyWindowStart, - index: params.index, - filters: params.filters, - language: params.language, - data_view_id: params.dataViewId, - alert_suppression: convertAlertSuppressionToSnake(params.alertSuppression), - }; - } - default: { - return assertUnreachable(params); - } - } -}; - -// TODO: separate out security solution defined common params from Alerting framework common params -// so we can explicitly specify the return type of this function -export const commonParamsCamelToSnake = (params: BaseRuleParams) => { - return { - description: params.description, - risk_score: params.riskScore, - severity: params.severity, - building_block_type: params.buildingBlockType, - namespace: params.namespace, - note: params.note, - license: params.license, - output_index: params.outputIndex, - timeline_id: params.timelineId, - timeline_title: params.timelineTitle, - meta: params.meta, - rule_name_override: params.ruleNameOverride, - timestamp_override: params.timestampOverride, - timestamp_override_fallback_disabled: params.timestampOverrideFallbackDisabled, - investigation_fields: migrateLegacyInvestigationFields(params.investigationFields), - author: params.author, - false_positives: params.falsePositives, - from: params.from, - rule_id: params.ruleId, - max_signals: params.maxSignals, - risk_score_mapping: params.riskScoreMapping, - severity_mapping: params.severityMapping, - threat: params.threat, - to: params.to, - references: params.references, - version: params.version, - exceptions_list: params.exceptionsList, - immutable: params.immutable, - rule_source: convertObjectKeysToSnakeCase(params.ruleSource), - related_integrations: params.relatedIntegrations ?? [], - required_fields: params.requiredFields ?? [], - setup: params.setup ?? '', - }; -}; - -export const internalRuleToAPIResponse = ( - rule: SanitizedRule | ResolvedSanitizedRule -): RequiredOptional => { - const executionSummary = createRuleExecutionSummary(rule); - - const isResolvedRule = (obj: unknown): obj is ResolvedSanitizedRule => - (obj as ResolvedSanitizedRule).outcome != null; - - const alertActions = rule.actions.map(transformAlertToRuleAction); - const throttle = transformFromAlertThrottle(rule); - const actions = transformToActionFrequency(alertActions, throttle); - - return { - // saved object properties - outcome: isResolvedRule(rule) ? rule.outcome : undefined, - alias_target_id: isResolvedRule(rule) ? rule.alias_target_id : undefined, - alias_purpose: isResolvedRule(rule) ? rule.alias_purpose : undefined, - // Alerting framework params - id: rule.id, - updated_at: rule.updatedAt.toISOString(), - updated_by: rule.updatedBy ?? 'elastic', - created_at: rule.createdAt.toISOString(), - created_by: rule.createdBy ?? 'elastic', - name: rule.name, - tags: rule.tags, - interval: rule.schedule.interval, - enabled: rule.enabled, - revision: rule.revision, - // Security solution shared rule params - ...commonParamsCamelToSnake(rule.params), - // Type specific security solution rule params - ...typeSpecificCamelToSnake(rule.params), - // Actions - throttle: undefined, - actions, - // Execution summary - execution_summary: executionSummary ?? undefined, - }; -}; - -export const convertPrebuiltRuleAssetToRuleResponse = ( - prebuiltRuleAsset: PrebuiltRuleAsset -): RuleResponse => { - const prebuiltRuleAssetDefaults = { - enabled: false, - risk_score_mapping: [], - severity_mapping: [], - interval: DEFAULT_INTERVAL, - to: DEFAULT_TO, - from: DEFAULT_FROM, - exceptions_list: [], - false_positives: [], - max_signals: DEFAULT_MAX_SIGNALS, - actions: [], - related_integrations: [], - required_fields: [], - setup: '', - note: '', - references: [], - threat: [], - tags: [], - author: [], - }; - - const immutable = true; - - const ruleResponseSpecificFields = { - id: uuidv4(), - updated_at: new Date(0).toISOString(), - updated_by: '', - created_at: new Date(0).toISOString(), - created_by: '', - immutable, - rule_source: convertObjectKeysToSnakeCase(convertImmutableToRuleSource(immutable)), - revision: 1, - }; - - return RuleResponse.parse({ - ...prebuiltRuleAssetDefaults, - ...prebuiltRuleAsset, - required_fields: addEcsToRequiredFields(prebuiltRuleAsset.required_fields), - ...ruleResponseSpecificFields, - }); -}; - -export const convertImmutableToRuleSource = (immutable: boolean): RuleSourceCamelCased => { - if (immutable) { - return { - type: 'external', - isCustomized: false, - }; - } - - return { - type: 'internal', - }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts index 61436a04c2675..536a314fa6c09 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts @@ -38,7 +38,7 @@ import { getMlRuleParams, getQueryRuleParams, getThreatRuleParams } from '../../ import { createRulesAndExceptionsStreamFromNdJson } from '../logic/import/create_rules_stream_from_ndjson'; import type { RuleExceptionsPromiseFromStreams } from '../logic/import/import_rules_utils'; -import { internalRuleToAPIResponse } from '../normalization/rule_converters'; +import { internalRuleToAPIResponse } from '../logic/detection_rules_client/converters/internal_rule_to_api_response'; type PromiseFromStreams = RuleToImport | Error; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts index 66fa635e768ad..bf6227ddcbfe8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts @@ -17,8 +17,6 @@ import type { SavedObjectsClientContract } from '@kbn/core/server'; import type { RuleAction } from '@kbn/securitysolution-io-ts-alerting-types'; import type { - AlertSuppression, - AlertSuppressionCamel, InvestigationFields, RequiredField, RequiredFieldInput, @@ -33,7 +31,7 @@ import type { BulkError, OutputError } from '../../routes/utils'; import { createBulkErrorObject } from '../../routes/utils'; import type { InvestigationFieldsCombined, RuleAlertType, RuleParams } from '../../rule_schema'; import { hasValidRuleType } from '../../rule_schema'; -import { internalRuleToAPIResponse } from '../normalization/rule_converters'; +import { internalRuleToAPIResponse } from '../logic/detection_rules_client/converters/internal_rule_to_api_response'; type PromiseFromStreams = RuleToImport | Error; const MAX_CONCURRENT_SEARCHES = 10; @@ -347,28 +345,6 @@ export const getInvalidConnectors = async ( return [Array.from(errors.values()), Array.from(rulesAcc.values())]; }; -export const convertAlertSuppressionToCamel = ( - input: AlertSuppression | undefined -): AlertSuppressionCamel | undefined => - input - ? { - groupBy: input.group_by, - duration: input.duration, - missingFieldsStrategy: input.missing_fields_strategy, - } - : undefined; - -export const convertAlertSuppressionToSnake = ( - input: AlertSuppressionCamel | undefined -): AlertSuppression | undefined => - input - ? { - group_by: input.groupBy, - duration: input.duration, - missing_fields_strategy: input.missingFieldsStrategy, - } - : undefined; - /** * In ESS 8.10.x "investigation_fields" are mapped as string[]. * For 8.11+ logic is added on read in our endpoints to migrate diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.ts index 298b7f62d2973..dd77122ac4560 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.ts @@ -31,7 +31,7 @@ import { type UnifiedQueryRuleParams, } from '../../rule_schema'; import { type BulkError, createBulkErrorObject } from '../../routes/utils'; -import { internalRuleToAPIResponse } from '../normalization/rule_converters'; +import { internalRuleToAPIResponse } from '../logic/detection_rules_client/converters/internal_rule_to_api_response'; export const transformValidateBulkError = ( ruleId: string, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts index c2faa464b75da..7b48e32bf9962 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts @@ -23,6 +23,7 @@ import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { DEFAULT_PREVIEW_INDEX, DETECTION_ENGINE_RULES_PREVIEW, + SERVER_APP_ID, } from '../../../../../../common/constants'; import { validateCreateRuleProps } from '../../../../../../common/api/detection_engine/rule_management'; import { RuleExecutionStatusEnum } from '../../../../../../common/api/detection_engine/rule_monitoring'; @@ -34,7 +35,6 @@ import { PreviewRulesSchema } from '../../../../../../common/api/detection_engin import type { StartPlugins, SetupPlugins } from '../../../../../plugin'; import { buildSiemResponse } from '../../../routes/utils'; -import { convertCreateAPIToInternalSchema } from '../../../rule_management'; import type { RuleParams } from '../../../rule_schema'; import { createPreviewRuleExecutionLogger } from './preview_rule_execution_logger'; import { parseInterval } from '../../../rule_types/utils/utils'; @@ -64,6 +64,8 @@ import { createSecurityRuleTypeWrapper } from '../../../rule_types/create_securi import { assertUnreachable } from '../../../../../../common/utility_types'; import { wrapScopedClusterClient } from './wrap_scoped_cluster_client'; import { wrapSearchSourceClient } from './wrap_search_source_client'; +import { applyRuleDefaults } from '../../../rule_management/logic/detection_rules_client/mergers/apply_rule_defaults'; +import { convertRuleResponseToAlertingRule } from '../../../rule_management/logic/detection_rules_client/converters/convert_rule_response_to_alerting_rule'; const PREVIEW_TIMEOUT_SECONDS = 60; const MAX_ROUTE_CONCURRENCY = 10; @@ -118,7 +120,7 @@ export const previewRulesRoute = ( }); } - const internalRule = convertCreateAPIToInternalSchema(request.body); + const internalRule = convertRuleResponseToAlertingRule(applyRuleDefaults(request.body)); const previewRuleParams = internalRule.params; const mlAuthz = buildMlAuthz({ @@ -237,6 +239,8 @@ export const previewRulesRoute = ( createdAt: new Date(), createdBy: username ?? 'preview-created-by', producer: 'preview-producer', + consumer: SERVER_APP_ID, + enabled: true, revision: 0, ruleTypeId, ruleTypeName, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts index 6ebc378dfd315..a310cb33497e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts @@ -12,10 +12,13 @@ import type { RuleResponseEndpointAction, ProcessesParams, } from '../../../../common/api/detection_engine'; -import type { KillOrSuspendProcessRequestBody } from '../../../../common/endpoint/types'; import { getErrorProcessAlerts, getIsolateAlerts, getProcessAlerts } from './utils'; import type { AlertsAction, ResponseActionAlerts } from './types'; import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services'; +import type { + ResponseActionParametersWithEntityId, + ResponseActionParametersWithPid, +} from '../../../../common/endpoint/types'; export const endpointResponseAction = async ( responseAction: RuleResponseEndpointAction, @@ -115,7 +118,9 @@ export const endpointResponseAction = async ( comment, endpoint_ids, alert_ids, - parameters: parameters as KillOrSuspendProcessRequestBody['parameters'], + parameters: parameters as + | ResponseActionParametersWithPid + | ResponseActionParametersWithEntityId, }, { hosts, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index b3000edf895dc..5d5065170deff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -5,11 +5,6 @@ * 2.0. */ import type { SanitizedRuleConfig } from '@kbn/alerting-plugin/common'; -import type { - RuleActionArrayCamel, - RuleActionNotifyWhen, - RuleActionThrottle, -} from '@kbn/securitysolution-io-ts-alerting-types'; import type { EQL_RULE_TYPE_ID, ESQL_RULE_TYPE_ID, @@ -22,12 +17,9 @@ import type { THRESHOLD_RULE_TYPE_ID, } from '@kbn/securitysolution-rules'; import * as z from 'zod'; +import type { CreateRuleData } from '@kbn/alerting-plugin/server/application/rule/methods/create'; +import type { UpdateRuleData } from '@kbn/alerting-plugin/server/application/rule/methods/update'; import { RuleResponseAction } from '../../../../../common/api/detection_engine'; -import type { - IsRuleEnabled, - RuleName, - RuleTagArray, -} from '../../../../../common/api/detection_engine/model/rule_schema'; import { AlertsIndex, AlertsIndexNamespace, @@ -334,29 +326,8 @@ export type AllRuleTypes = | typeof THRESHOLD_RULE_TYPE_ID | typeof NEW_TERMS_RULE_TYPE_ID; -export interface InternalRuleCreate { - name: RuleName; - tags: RuleTagArray; - alertTypeId: AllRuleTypes; +export type InternalRuleCreate = CreateRuleData & { consumer: typeof SERVER_APP_ID; - schedule: { - interval: string; - }; - enabled: IsRuleEnabled; - actions: RuleActionArrayCamel; - params: RuleParams; - throttle?: RuleActionThrottle | null; - notifyWhen?: RuleActionNotifyWhen | null; -} +}; -export interface InternalRuleUpdate { - name: RuleName; - tags: RuleTagArray; - schedule: { - interval: string; - }; - actions: RuleActionArrayCamel; - params: RuleParams; - throttle?: RuleActionThrottle | null; - notifyWhen?: RuleActionNotifyWhen | null; -} +export type InternalRuleUpdate = UpdateRuleData; diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.test.ts b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.test.ts index 526f3d595d5a0..114d51bfae6a4 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.test.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.test.ts @@ -55,7 +55,6 @@ describe(`installRiskScoresRoute - ${RiskScoreEntity.host}`, () => { let server: ReturnType; let { context } = requestContextMock.createTools(); const logger = { error: jest.fn() } as unknown as Logger; - const security = undefined; const mockSpaceId = 'mockSpaceId'; beforeAll(async () => { @@ -71,7 +70,7 @@ describe(`installRiskScoresRoute - ${RiskScoreEntity.host}`, () => { }, }); - installRiskScoresRoute(server.router, logger, security); + installRiskScoresRoute(server.router, logger); await server.inject(request, requestContextMock.convertContext(context)); }); @@ -120,7 +119,6 @@ describe(`installRiskScoresRoute - ${RiskScoreEntity.user}`, () => { let server: ReturnType; let { context } = requestContextMock.createTools(); const logger = { error: jest.fn() } as unknown as Logger; - const security = undefined; const mockSpaceId = 'mockSpaceId'; beforeAll(async () => { @@ -136,7 +134,7 @@ describe(`installRiskScoresRoute - ${RiskScoreEntity.user}`, () => { }, }); - installRiskScoresRoute(server.router, logger, security); + installRiskScoresRoute(server.router, logger); await server.inject(request, requestContextMock.convertContext(context)); }); diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.ts index 627a9afd4e871..de6675985fc00 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/onboarding/routes/install_risk_scores.ts @@ -11,18 +11,12 @@ import type { Logger } from '@kbn/core/server'; import { APP_ID, INTERNAL_RISK_SCORE_URL } from '../../../../../common/constants'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import type { SetupPlugins } from '../../../../plugin'; - import { buildSiemResponse } from '../../../detection_engine/routes/utils'; import { installRiskScoreModule } from '../helpers/install_risk_score_module'; import { onboardingRiskScoreRequestBody } from '../../../../../common/api/entity_analytics/risk_score'; -export const installRiskScoresRoute = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - security: SetupPlugins['security'] -) => { +export const installRiskScoresRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .post({ access: 'internal', diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts index bf265e8638de1..300012738134d 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.test.ts @@ -5,12 +5,10 @@ * 2.0. */ import type { Logger } from '@kbn/core/server'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { PREBUILT_SAVED_OBJECTS_BULK_CREATE } from '../../../../../common/constants'; import { serverMock, requestContextMock, - mockGetCurrentUser, requestMock, } from '../../../detection_engine/routes/__mocks__'; import { getEmptySavedObjectsResponse } from '../../../detection_engine/routes/__mocks__/request_responses'; @@ -52,7 +50,6 @@ const createPrebuiltSavedObjectsRequest = (savedObjectTemplate: string) => describe('createPrebuiltSavedObjects', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; let { clients, context } = requestContextMock.createTools(); const logger = { error: jest.fn() } as unknown as Logger; @@ -62,16 +59,9 @@ describe('createPrebuiltSavedObjects', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - clients.savedObjectsClient.bulkCreate.mockResolvedValue(getEmptySavedObjectsResponse()); - createPrebuiltSavedObjectsRoute(server.router, logger, securitySetup); + createPrebuiltSavedObjectsRoute(server.router, logger); }); it.each([['hostRiskScoreDashboards'], ['userRiskScoreDashboards']])( diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts index 568489155707a..a17669af734fe 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/create_prebuilt_saved_objects.ts @@ -10,8 +10,6 @@ import type { Logger } from '@kbn/core/server'; import { PREBUILT_SAVED_OBJECTS_BULK_CREATE } from '../../../../../common/constants'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import type { SetupPlugins } from '../../../../plugin'; - import { buildSiemResponse } from '../../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../../timeline/utils/common'; @@ -20,8 +18,7 @@ import { createPrebuiltSavedObjectsRequestBody } from '../../../../../common/api export const createPrebuiltSavedObjectsRoute = ( router: SecuritySolutionPluginRouter, - logger: Logger, - security: SetupPlugins['security'] + logger: Logger ) => { router.versioned .post({ @@ -46,7 +43,7 @@ export const createPrebuiltSavedObjectsRoute = ( const spaceId = securitySolution?.getSpaceId(); - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const savedObjectsClient = (await frameworkRequest.context.core).savedObjects.client; const result = await bulkCreateSavedObjects({ savedObjectsClient, diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.test.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.test.ts index 363aba02536ea..37c2417340fcd 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.test.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.test.ts @@ -4,12 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { PREBUILT_SAVED_OBJECTS_BULK_DELETE } from '../../../../../common/constants'; import { serverMock, requestContextMock, - mockGetCurrentUser, requestMock, } from '../../../detection_engine/routes/__mocks__'; import { deletePrebuiltSavedObjectsRoute } from './delete_prebuilt_saved_objects'; @@ -48,7 +46,6 @@ const deletePrebuiltSavedObjectsRequest = (savedObjectTemplate: string) => describe('deletePrebuiltSavedObjects', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; let { clients, context } = requestContextMock.createTools(); beforeEach(() => { @@ -57,16 +54,9 @@ describe('deletePrebuiltSavedObjects', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - clients.savedObjectsClient.delete.mockResolvedValue(''); - deletePrebuiltSavedObjectsRoute(server.router, securitySetup); + deletePrebuiltSavedObjectsRoute(server.router); }); it('should delete legacy hostRiskScoreDashboards', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.ts b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.ts index c78d1f292afe6..bd7ae03191ea5 100644 --- a/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/lib/risk_score/prebuilt_saved_objects/routes/delete_prebuilt_saved_objects.ts @@ -10,18 +10,13 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { PREBUILT_SAVED_OBJECTS_BULK_DELETE } from '../../../../../common/constants'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import type { SetupPlugins } from '../../../../plugin'; - import { buildSiemResponse } from '../../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../../timeline/utils/common'; import { bulkDeleteSavedObjects } from '../helpers/bulk_delete_saved_objects'; import { deletePrebuiltSavedObjectsRequestBody } from '../../../../../common/api/entity_analytics/risk_score'; -export const deletePrebuiltSavedObjectsRoute = ( - router: SecuritySolutionPluginRouter, - security: SetupPlugins['security'] -) => { +export const deletePrebuiltSavedObjectsRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .post({ access: 'internal', @@ -42,7 +37,7 @@ export const deletePrebuiltSavedObjectsRoute = ( const spaceId = securitySolution?.getSpaceId(); - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const savedObjectsClient = (await frameworkRequest.context.core).savedObjects.client; const res = await bulkDeleteSavedObjects({ diff --git a/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.test.ts b/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.test.ts index c546d2fe63a49..232c09e76ee32 100644 --- a/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.test.ts +++ b/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.test.ts @@ -5,12 +5,10 @@ * 2.0. */ import type { Logger } from '@kbn/core/server'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { INTERNAL_TAGS_URL } from '../../../../common/constants'; import { serverMock, requestContextMock, - mockGetCurrentUser, requestMock, } from '../../detection_engine/routes/__mocks__'; import { mockGetTagsResult } from '../__mocks__'; @@ -18,7 +16,6 @@ import { createTagRoute } from './create_tag'; describe('createTagRoute', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; const { context } = requestContextMock.createTools(); const logger = { error: jest.fn() } as unknown as Logger; @@ -34,14 +31,7 @@ describe('createTagRoute', () => { jest.clearAllMocks(); server = serverMock.create(); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - - createTagRoute(server.router, logger, securitySetup); + createTagRoute(server.router, logger); }); it('should return tags with the exact name', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts b/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts index 97499b76ae354..1604b4374b984 100644 --- a/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts +++ b/x-pack/plugins/security_solution/server/lib/tags/routes/create_tag.ts @@ -10,18 +10,13 @@ import { i18n } from '@kbn/i18n'; import { transformError } from '@kbn/securitysolution-es-utils'; import { createTagRequest } from '../../../../common/api/tags'; import { INTERNAL_TAGS_URL } from '../../../../common/constants'; -import type { SetupPlugins } from '../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { buildRouteValidationWithExcess } from '../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../timeline/utils/common'; import { createTag } from '../saved_objects'; -export const createTagRoute = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - security: SetupPlugins['security'] -) => { +export const createTagRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .put({ path: INTERNAL_TAGS_URL, @@ -36,7 +31,7 @@ export const createTagRoute = ( validate: { request: { body: buildRouteValidationWithExcess(createTagRequest) } }, }, async (context, request, response) => { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const savedObjectsClient = (await frameworkRequest.context.core).savedObjects.client; const { name: tagName, description, color } = request.body; try { diff --git a/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.test.ts b/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.test.ts index dd18ad8257867..fab00d4db059b 100644 --- a/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.test.ts +++ b/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.test.ts @@ -5,12 +5,10 @@ * 2.0. */ import type { Logger, SavedObjectsFindResponse } from '@kbn/core/server'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { INTERNAL_TAGS_URL, SECURITY_TAG_NAME } from '../../../../common/constants'; import { serverMock, requestContextMock, - mockGetCurrentUser, requestMock, } from '../../detection_engine/routes/__mocks__'; import { mockGetTagsResult } from '../__mocks__'; @@ -18,7 +16,6 @@ import { getTagsByNameRoute } from './get_tags_by_name'; describe('getTagsByNameRoute', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; const { context } = requestContextMock.createTools(); const logger = { error: jest.fn() } as unknown as Logger; @@ -36,14 +33,7 @@ describe('getTagsByNameRoute', () => { jest.clearAllMocks(); server = serverMock.create(); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - - getTagsByNameRoute(server.router, logger, securitySetup); + getTagsByNameRoute(server.router, logger); }); it('should return tags with the exact name', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts b/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts index cd07a71db5a1c..75ae24d0eacd5 100644 --- a/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts +++ b/x-pack/plugins/security_solution/server/lib/tags/routes/get_tags_by_name.ts @@ -10,18 +10,13 @@ import { i18n } from '@kbn/i18n'; import { transformError } from '@kbn/securitysolution-es-utils'; import { getTagsByNameRequest } from '../../../../common/api/tags'; import { INTERNAL_TAGS_URL } from '../../../../common/constants'; -import type { SetupPlugins } from '../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { buildRouteValidationWithExcess } from '../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../timeline/utils/common'; import { findTagsByName } from '../saved_objects'; -export const getTagsByNameRoute = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - security: SetupPlugins['security'] -) => { +export const getTagsByNameRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .get({ path: INTERNAL_TAGS_URL, @@ -36,7 +31,7 @@ export const getTagsByNameRoute = ( validate: { request: { query: buildRouteValidationWithExcess(getTagsByNameRequest) } }, }, async (context, request, response) => { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const savedObjectsClient = (await frameworkRequest.context.core).savedObjects.client; const { name: tagName } = request.query; diff --git a/x-pack/plugins/security_solution/server/lib/tags/routes/index.ts b/x-pack/plugins/security_solution/server/lib/tags/routes/index.ts index bfcdf518ee905..f7677388856a7 100644 --- a/x-pack/plugins/security_solution/server/lib/tags/routes/index.ts +++ b/x-pack/plugins/security_solution/server/lib/tags/routes/index.ts @@ -6,16 +6,11 @@ */ import type { Logger } from '@kbn/core/server'; -import type { SetupPlugins } from '../../../plugin_contract'; import type { SecuritySolutionPluginRouter } from '../../../types'; import { createTagRoute } from './create_tag'; import { getTagsByNameRoute } from './get_tags_by_name'; -export const registerTagsRoutes = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - security: SetupPlugins['security'] -) => { - getTagsByNameRoute(router, logger, security); - createTagRoute(router, logger, security); +export const registerTagsRoutes = (router: SecuritySolutionPluginRouter, logger: Logger) => { + getTagsByNameRoute(router, logger); + createTagRoute(router, logger); }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts index 604e9dabe0d62..ae1cff768fded 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.ts @@ -13,7 +13,6 @@ import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; import { TIMELINE_DRAFT_URL } from '../../../../../../common/constants'; import { buildFrameworkRequest } from '../../../utils/common'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import { getDraftTimeline, @@ -24,11 +23,7 @@ import { import { draftTimelineDefaults } from '../../../utils/default_timeline'; import { cleanDraftTimelineSchema, TimelineType } from '../../../../../../common/api/timeline'; -export const cleanDraftTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const cleanDraftTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .post({ path: TIMELINE_DRAFT_URL, @@ -45,7 +40,7 @@ export const cleanDraftTimelinesRoute = ( version: '2023-10-31', }, async (context, request, response) => { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const siemResponse = buildSiemResponse(response); try { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts index 93e5d9fe84b00..6619e4a0eb18b 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.ts @@ -12,17 +12,12 @@ import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; import { TIMELINE_DRAFT_URL } from '../../../../../../common/constants'; import { buildFrameworkRequest } from '../../../utils/common'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import { getDraftTimeline, persistTimeline } from '../../../saved_object/timelines'; import { draftTimelineDefaults } from '../../../utils/default_timeline'; import { getDraftTimelineSchema } from '../../../../../../common/api/timeline'; -export const getDraftTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const getDraftTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .get({ path: TIMELINE_DRAFT_URL, @@ -39,7 +34,7 @@ export const getDraftTimelinesRoute = ( version: '2023-10-31', }, async (context, request, response) => { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const siemResponse = buildSiemResponse(response); try { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/index.ts index 8fa7e7a8f31d6..6b44496b6c3c4 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { SetupPlugins } from '../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../types'; import type { ConfigType } from '../../..'; import { @@ -28,30 +27,26 @@ import { persistNoteRoute, deleteNoteRoute, getNotesRoute } from './notes'; import { persistPinnedEventRoute } from './pinned_events'; -export function registerTimelineRoutes( - router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'] -) { - createTimelinesRoute(router, config, security); - patchTimelinesRoute(router, config, security); - - importTimelinesRoute(router, config, security); - exportTimelinesRoute(router, config, security); - getDraftTimelinesRoute(router, config, security); - getTimelineRoute(router, config, security); - resolveTimelineRoute(router, config, security); - getTimelinesRoute(router, config, security); - cleanDraftTimelinesRoute(router, config, security); - deleteTimelinesRoute(router, config, security); - persistFavoriteRoute(router, config, security); - copyTimelineRoute(router, config, security); - - installPrepackedTimelinesRoute(router, config, security); - - persistNoteRoute(router, config, security); - deleteNoteRoute(router, config, security); - getNotesRoute(router, config, security); - - persistPinnedEventRoute(router, config, security); +export function registerTimelineRoutes(router: SecuritySolutionPluginRouter, config: ConfigType) { + createTimelinesRoute(router, config); + patchTimelinesRoute(router, config); + + importTimelinesRoute(router, config); + exportTimelinesRoute(router, config); + getDraftTimelinesRoute(router, config); + getTimelineRoute(router, config); + resolveTimelineRoute(router, config); + getTimelinesRoute(router, config); + cleanDraftTimelinesRoute(router, config); + deleteTimelinesRoute(router, config); + persistFavoriteRoute(router, config); + copyTimelineRoute(router, config); + + installPrepackedTimelinesRoute(router, config); + + persistNoteRoute(router, config); + deleteNoteRoute(router, config); + getNotesRoute(router, config); + + persistPinnedEventRoute(router, config); } diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts index 6cab82872b924..318d8950bc619 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/delete_note.ts @@ -10,7 +10,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../types'; import { NOTE_URL } from '../../../../../common/constants'; -import type { SetupPlugins } from '../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../utils/build_validation/route_validation'; import type { ConfigType } from '../../../..'; @@ -20,11 +19,7 @@ import { buildFrameworkRequest } from '../../utils/common'; import { deleteNoteSchema } from '../../../../../common/api/timeline'; import { deleteNote } from '../../saved_object/notes'; -export const deleteNoteRoute = ( - router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'] -) => { +export const deleteNoteRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => { router.versioned .delete({ path: NOTE_URL, @@ -44,7 +39,7 @@ export const deleteNoteRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const noteId = request.body?.noteId ?? ''; const noteIds = request.body?.noteIds ?? null; if (noteIds != null) { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts index 97aecc06ef198..f230d0832a96c 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts @@ -10,18 +10,13 @@ import type { SecuritySolutionPluginRouter } from '../../../../types'; import { NOTE_URL } from '../../../../../common/constants'; import type { ConfigType } from '../../../..'; -import type { SetupPlugins } from '../../../../plugin'; import { buildSiemResponse } from '../../../detection_engine/routes/utils'; import { buildFrameworkRequest, getNotesPaginated } from '../../utils/common'; import { getAllSavedNote, MAX_UNASSOCIATED_NOTES } from '../../saved_object/notes'; import { noteSavedObjectType } from '../../saved_object_mappings/notes'; -export const getNotesRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const getNotesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .get({ path: NOTE_URL, @@ -40,7 +35,7 @@ export const getNotesRoute = ( async (context, request, response) => { try { const queryParams = request.query; - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const documentIds = queryParams.documentIds ?? null; if (documentIds != null) { if (Array.isArray(documentIds)) { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts index e4cf4b8a9c777..1bd91306a7419 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/persist_note.ts @@ -10,7 +10,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../types'; import { NOTE_URL } from '../../../../../common/constants'; -import type { SetupPlugins } from '../../../../plugin'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import type { ConfigType } from '../../../..'; @@ -20,11 +19,7 @@ import { buildFrameworkRequest } from '../../utils/common'; import { persistNoteWithoutRefSchema } from '../../../../../common/api/timeline'; import { persistNote } from '../../saved_object/notes'; -export const persistNoteRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const persistNoteRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .patch({ path: NOTE_URL, @@ -44,7 +39,7 @@ export const persistNoteRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const { note } = request.body; const noteId = request.body?.noteId ?? null; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts index 529f5d830e803..a5739c49a34ea 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/pinned_events/persist_pinned_event.ts @@ -10,7 +10,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../types'; import { PINNED_EVENT_URL } from '../../../../../common/constants'; -import type { SetupPlugins } from '../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../utils/build_validation/route_validation'; import type { ConfigType } from '../../../..'; @@ -22,8 +21,7 @@ import { persistPinnedEventOnTimeline } from '../../saved_object/pinned_events'; export const persistPinnedEventRoute = ( router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'] + config: ConfigType ) => { router.versioned .patch({ @@ -44,7 +42,7 @@ export const persistPinnedEventRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const { eventId } = request.body; const pinnedEventId = request.body?.pinnedEventId ?? null; const timelineId = request.body?.timelineId ?? null; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts index 763833a0ab4e8..7a4e7a41a1ee7 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts @@ -6,13 +6,11 @@ */ import { createPromiseFromStreams } from '@kbn/utils'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import type { FrameworkRequest } from '../../../../framework'; import { createMockConfig, requestContextMock, - mockGetCurrentUser, } from '../../../../detection_engine/routes/__mocks__'; import { addPrepackagedRulesRequest, @@ -27,7 +25,6 @@ import type { ImportTimelineResultSchema } from '../../../../../../common/api/ti jest.mock('../../timelines/import_timelines/helpers'); describe('installPrepackagedTimelines', () => { - let securitySetup: SecurityPluginSetup; let frameworkRequest: FrameworkRequest; const spyInstallPrepackagedTimelines = jest.spyOn(helpers, 'installPrepackagedTimelines'); @@ -37,13 +34,6 @@ describe('installPrepackagedTimelines', () => { const mockFileName = 'prepackaged_timelines.ndjson'; beforeEach(async () => { - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); jest.doMock('./helpers', () => { @@ -56,7 +46,6 @@ describe('installPrepackagedTimelines', () => { const request = addPrepackagedRulesRequest(); frameworkRequest = await buildFrameworkRequest( requestContextMock.convertContext(context), - securitySetup, request ); }); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts index 17086ca5a317d..9513bd49b4e6d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts @@ -5,8 +5,6 @@ * 2.0. */ -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; - import { serverMock, requestContextMock, @@ -14,7 +12,6 @@ import { } from '../../../../detection_engine/routes/__mocks__'; import { - mockGetCurrentUser, mockCheckTimelinesStatusBeforeInstallResult, mockCheckTimelinesStatusAfterInstallResult, } from '../../../__mocks__/import_timelines'; @@ -39,7 +36,6 @@ jest.mock('../../../utils/check_timelines_status', () => { describe('installPrepackagedTimelines', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; let { context } = requestContextMock.createTools(); beforeEach(() => { @@ -49,14 +45,7 @@ describe('installPrepackagedTimelines', () => { server = serverMock.create(); context = requestContextMock.createTools().context; - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - - installPrepackedTimelinesRoute(server.router, createMockConfig(), securitySetup); + installPrepackedTimelinesRoute(server.router, createMockConfig()); }); test('should call installPrepackagedTimelines ', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts index 88d868968e9bb..0b150b4e47f56 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.ts @@ -12,7 +12,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_PREPACKAGED_URL } from '../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../plugin'; import type { ConfigType } from '../../../../../config'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; @@ -27,8 +26,7 @@ export { installPrepackagedTimelines } from './helpers'; export const installPrepackedTimelinesRoute = ( router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'] + config: ConfigType ) => { router.versioned .post({ @@ -49,7 +47,7 @@ export const installPrepackedTimelinesRoute = ( }, async (context, request, response) => { try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const prepackagedTimelineStatus = await checkTimelinesStatus(frameworkRequest); const [validatedprepackagedTimelineStatus, prepackagedTimelineStatusError] = validate( prepackagedTimelineStatus, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts index 0e288e0099596..5bdb4e0f93d67 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/copy_timeline/index.ts @@ -11,17 +11,12 @@ import type { ConfigType } from '../../../../..'; import { copyTimelineSchema } from '../../../../../../common/api/timeline'; import { copyTimeline } from '../../../saved_object/timelines'; import type { SecuritySolutionPluginRouter } from '../../../../../types'; -import type { SetupPlugins } from '../../../../../plugin'; import { TIMELINE_COPY_URL } from '../../../../../../common/constants'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../../utils/common'; -export const copyTimelineRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const copyTimelineRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .post({ path: TIMELINE_COPY_URL, @@ -41,7 +36,7 @@ export const copyTimelineRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const { timeline, timelineIdToCopy } = request.body; const copiedTimeline = await copyTimeline(frameworkRequest, timeline, timelineIdToCopy); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts index d444983e7b00a..9799d2c6bdf3b 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts @@ -12,7 +12,6 @@ import type { FrameworkRequest } from '../../../../framework'; import type { SavedTimeline, Note } from '../../../../../../common/api/timeline'; import { mockTemplate, mockTimeline } from '../../../__mocks__/create_timelines'; import { buildFrameworkRequest } from '../../../utils/common'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { requestContextMock } from '../../../../detection_engine/routes/__mocks__'; import { getCreateTimelinesRequest, @@ -62,23 +61,14 @@ jest.mock('../../../saved_object/notes/persist_notes', () => ({ })); describe('createTimelines', () => { - let securitySetup: SecurityPluginSetup; let frameworkRequest: FrameworkRequest; beforeAll(async () => { - securitySetup = { - authc: { - getCurrentUser: jest.fn(), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - const { context } = requestContextMock.createTools(); const mockRequest = getCreateTimelinesRequest(createTimelineWithoutTimelineId); frameworkRequest = await buildFrameworkRequest( requestContextMock.convertContext(context), - securitySetup, mockRequest ); Date.now = jest.fn().mockReturnValue(new Date('2020-11-04T11:37:31.655Z')); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts index 91b191c6ead0f..0fcf286661b96 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.ts @@ -12,7 +12,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_URL } from '../../../../../../common/constants'; import type { ConfigType } from '../../../../..'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; @@ -29,11 +28,7 @@ import type { CreateTimelinesResponse } from '../../../../../../common/api/timel export * from './helpers'; -export const createTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const createTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .post({ path: TIMELINE_URL, @@ -55,7 +50,7 @@ export const createTimelinesRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const { timelineId, timeline, version } = request.body; const { templateTimelineId, templateTimelineVersion, timelineType, title, status } = diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts index 602d29ae061ab..7f6339ee25929 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/delete_timelines/index.ts @@ -10,18 +10,13 @@ import { buildRouteValidationWithExcess } from '../../../../../utils/build_valid import type { ConfigType } from '../../../../..'; import { deleteTimelinesSchema } from '../../../../../../common/api/timeline'; import type { SecuritySolutionPluginRouter } from '../../../../../types'; -import type { SetupPlugins } from '../../../../../plugin'; import { TIMELINE_URL } from '../../../../../../common/constants'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; import { buildFrameworkRequest } from '../../../utils/common'; import { deleteTimeline } from '../../../saved_object/timelines'; -export const deleteTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'] -) => { +export const deleteTimelinesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => { router.versioned .delete({ path: TIMELINE_URL, @@ -41,7 +36,7 @@ export const deleteTimelinesRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const { savedObjectIds, searchIds } = request.body; await deleteTimeline(frameworkRequest, savedObjectIds, searchIds); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts index 044fc77caef80..13b6e0f68fefc 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts @@ -23,8 +23,6 @@ import { TIMELINE_EXPORT_URL } from '../../../../../../common/constants'; import { convertSavedObjectToSavedNote } from '../../../saved_object/notes/saved_object'; import { convertSavedObjectToSavedPinnedEvent } from '../../../saved_object/pinned_events'; import { convertSavedObjectToSavedTimeline } from '../../../saved_object/timelines/convert_saved_object_to_savedtimeline'; -import { mockGetCurrentUser } from '../../../__mocks__/import_timelines'; -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; jest.mock('../../../saved_object/timelines/convert_saved_object_to_savedtimeline', () => { return { @@ -48,17 +46,10 @@ jest.mock('../../../saved_object/pinned_events', () => { describe('export timelines', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let securitySetup: SecurityPluginSetup; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; clients.savedObjectsClient.bulkGet.mockResolvedValue(mockTimelinesSavedObjects()); (convertSavedObjectToSavedTimeline as unknown as jest.Mock).mockReturnValue(mockTimelines()); @@ -66,7 +57,7 @@ describe('export timelines', () => { (convertSavedObjectToSavedPinnedEvent as unknown as jest.Mock).mockReturnValue( mockPinnedEvents() ); - exportTimelinesRoute(server.router, createMockConfig(), securitySetup); + exportTimelinesRoute(server.router, createMockConfig()); }); describe('status codes', () => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts index 6f02c312b7bc7..7af6b7be0cdd0 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts @@ -17,17 +17,12 @@ import { } from '../../../../../../common/api/timeline'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import { buildFrameworkRequest } from '../../../utils/common'; -import type { SetupPlugins } from '../../../../../plugin'; import { getExportTimelineByObjectIds } from './helpers'; export * from './helpers'; -export const exportTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'] -) => { +export const exportTimelinesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => { router.versioned .post({ path: TIMELINE_EXPORT_URL, @@ -49,7 +44,7 @@ export const exportTimelinesRoute = ( async (context, request, response) => { try { const siemResponse = buildSiemResponse(response); - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const exportSizeLimit = config.maxTimelineImportExportSize; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts index d439bdd300c4b..78df97fa441be 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts @@ -5,8 +5,6 @@ * 2.0. */ -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; - import { serverMock, requestContextMock, @@ -14,7 +12,6 @@ import { } from '../../../../detection_engine/routes/__mocks__'; import { getTimelineOrNull, getTimelineTemplateOrNull } from '../../../saved_object/timelines'; -import { mockGetCurrentUser } from '../../../__mocks__/import_timelines'; import { getTimelineRequest } from '../../../__mocks__/request_responses'; import { getTimelineRoute } from '.'; @@ -27,7 +24,6 @@ jest.mock('../../../saved_object/timelines', () => ({ describe('get timeline', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; let { context } = requestContextMock.createTools(); beforeEach(() => { @@ -37,14 +33,7 @@ describe('get timeline', () => { server = serverMock.create(); context = requestContextMock.createTools().context; - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - - getTimelineRoute(server.router, createMockConfig(), securitySetup); + getTimelineRoute(server.router, createMockConfig()); }); test('should call getTimelineTemplateOrNull if templateTimelineId is given', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts index 25cf3c895f291..321e6aafe4903 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.ts @@ -11,7 +11,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_URL } from '../../../../../../common/constants'; import type { ConfigType } from '../../../../..'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; @@ -24,11 +23,7 @@ import type { ResolvedTimelineWithOutcomeSavedObject, } from '../../../../../../common/api/timeline'; -export const getTimelineRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const getTimelineRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .get({ path: TIMELINE_URL, @@ -46,7 +41,7 @@ export const getTimelineRoute = ( }, async (context, request, response) => { try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const query = request.query ?? {}; const { template_timeline_id: templateTimelineId, id } = query; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts index 012427846f35e..b006b77c9a595 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts @@ -5,18 +5,13 @@ * 2.0. */ -import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; - import { serverMock, requestContextMock, createMockConfig, } from '../../../../detection_engine/routes/__mocks__'; import { getAllTimeline } from '../../../saved_object/timelines'; - -import { mockGetCurrentUser } from '../../../__mocks__/import_timelines'; import { getTimelineRequest } from '../../../__mocks__/request_responses'; - import { getTimelinesRoute } from '.'; jest.mock('../../../saved_object/timelines', () => ({ @@ -25,7 +20,6 @@ jest.mock('../../../saved_object/timelines', () => ({ describe('get all timelines', () => { let server: ReturnType; - let securitySetup: SecurityPluginSetup; let { context } = requestContextMock.createTools(); beforeEach(() => { @@ -35,14 +29,7 @@ describe('get all timelines', () => { server = serverMock.create(); context = requestContextMock.createTools().context; - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - - getTimelinesRoute(server.router, createMockConfig(), securitySetup); + getTimelinesRoute(server.router, createMockConfig()); }); test('should get the total count', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts index 02cb8ff41f1a3..97d9bf51a0876 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.ts @@ -14,7 +14,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINES_URL } from '../../../../../../common/constants'; import type { ConfigType } from '../../../../..'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; @@ -23,11 +22,7 @@ import { buildFrameworkRequest, escapeHatch, throwErrors } from '../../../utils/ import { getAllTimeline } from '../../../saved_object/timelines'; import { getTimelinesQuerySchema } from '../../../../../../common/api/timeline'; -export const getTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const getTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .get({ path: TIMELINES_URL, @@ -47,7 +42,7 @@ export const getTimelinesRoute = ( const customHttpRequestError = (message: string) => new CustomHttpRequestError(message, 400); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const queryParams = pipe( getTimelinesQuerySchema.decode(request.query), fold(throwErrors(customHttpRequestError), identity) diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts index 8d6326af6c17c..be5ebc44e7faa 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts @@ -13,7 +13,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_IMPORT_URL } from '../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../plugin'; import type { ConfigType } from '../../../../../config'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; @@ -24,11 +23,7 @@ import { buildFrameworkRequest } from '../../../utils/common'; export { importTimelines } from './helpers'; -export const importTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'] -) => { +export const importTimelinesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => { router.versioned .post({ path: `${TIMELINE_IMPORT_URL}`, @@ -66,7 +61,7 @@ export const importTimelinesRoute = ( body: `Invalid file extension ${fileExtension}`, }); } - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const res = await importTimelines( file as unknown as Readable, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts index c8b9c26536444..1c3cba9fdcb91 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.ts @@ -11,7 +11,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_URL } from '../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import type { ConfigType } from '../../../../..'; @@ -23,11 +22,7 @@ import { createTimelines } from '../create_timelines'; import { CompareTimelinesStatus } from '../../../utils/compare_timelines_status'; import type { PatchTimelinesResponse } from '../../../../../../common/api/timeline'; -export const patchTimelinesRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const patchTimelinesRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .patch({ path: TIMELINE_URL, @@ -47,7 +42,7 @@ export const patchTimelinesRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const { timelineId, timeline, version } = request.body; const { templateTimelineId, templateTimelineVersion, timelineType, title, status } = timeline; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts index 746987caa3759..3b220ccf57e20 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/persist_favorite/index.ts @@ -10,7 +10,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_FAVORITE_URL } from '../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import type { ConfigType } from '../../../../..'; @@ -20,11 +19,7 @@ import { buildFrameworkRequest } from '../../../utils/common'; import { persistFavorite } from '../../../saved_object/timelines'; import { TimelineType, persistFavoriteSchema } from '../../../../../../common/api/timeline'; -export const persistFavoriteRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const persistFavoriteRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .patch({ path: TIMELINE_FAVORITE_URL, @@ -44,7 +39,7 @@ export const persistFavoriteRoute = ( const siemResponse = buildSiemResponse(response); try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const { timelineId, templateTimelineId, templateTimelineVersion, timelineType } = request.body; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts index 7572e72cfb9ac..09549f9b9034f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/resolve_timeline/index.ts @@ -11,7 +11,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { TIMELINE_RESOLVE_URL } from '../../../../../../common/constants'; import type { ConfigType } from '../../../../..'; -import type { SetupPlugins } from '../../../../../plugin'; import { buildRouteValidationWithExcess } from '../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../detection_engine/routes/utils'; @@ -24,11 +23,7 @@ import type { ResolvedTimelineWithOutcomeSavedObject, } from '../../../../../../common/api/timeline'; -export const resolveTimelineRoute = ( - router: SecuritySolutionPluginRouter, - _: ConfigType, - security: SetupPlugins['security'] -) => { +export const resolveTimelineRoute = (router: SecuritySolutionPluginRouter, _: ConfigType) => { router.versioned .get({ path: TIMELINE_RESOLVE_URL, @@ -46,7 +41,7 @@ export const resolveTimelineRoute = ( }, async (context, request, response) => { try { - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const frameworkRequest = await buildFrameworkRequest(context, request); const query = request.query ?? {}; const { template_timeline_id: templateTimelineId, id } = query; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts index b9f1aaafbf969..cfa804b848fcd 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts @@ -14,17 +14,16 @@ import { schema } from '@kbn/config-schema'; import type { KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; import { formatErrors } from '@kbn/securitysolution-io-ts-utils'; -import type { SetupPlugins, StartPlugins } from '../../../plugin'; import type { FrameworkRequest } from '../../framework'; export const buildFrameworkRequest = async ( context: RequestHandlerContext, - security: StartPlugins['security'] | SetupPlugins['security'] | undefined, request: KibanaRequest ): Promise => { - const savedObjectsClient = (await context.core).savedObjects.client; - const user = await security?.authc.getCurrentUser(request); + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; + const user = coreContext.security.authc.getCurrentUser(); return set( 'user', diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 5b5b833dd2d4b..be4d5ae275d83 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -643,7 +643,7 @@ export class Plugin implements ISecuritySolutionPlugin { logger ), endpointFleetServicesFactory, - security: plugins.security, + security: core.security, alerting: plugins.alerting, config, cases: plugins.cases, diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index 1f36f7ecff234..00db7dcb2c3e8 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -65,8 +65,8 @@ export class RequestContextFactory implements IRequestContextFactory { const { lists, ruleRegistry, security } = plugins; - const [, startPlugins] = await core.getStartServices(); - const frameworkRequest = await buildFrameworkRequest(context, security, request); + const [_, startPlugins] = await core.getStartServices(); + const frameworkRequest = await buildFrameworkRequest(context, request); const coreContext = await context.core; const licensing = await context.licensing; @@ -122,10 +122,11 @@ export class RequestContextFactory implements IRequestContextFactory { savedObjectsClient: coreContext.savedObjects.client, }); - return createDetectionRulesClient( - startPlugins.alerting.getRulesClientWithRequest(request), - mlAuthz - ); + return createDetectionRulesClient({ + rulesClient: startPlugins.alerting.getRulesClientWithRequest(request), + savedObjectsClient: coreContext.savedObjects.client, + mlAuthz, + }); }), getDetectionEngineHealthClient: memoize(() => @@ -148,7 +149,7 @@ export class RequestContextFactory implements IRequestContextFactory { return null; } - const username = security?.authc.getCurrentUser(request)?.username || 'elastic'; + const username = coreContext.security.authc.getCurrentUser()?.username || 'elastic'; return lists.getExceptionListClient(coreContext.savedObjects.client, username); }, diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index bc1a26534cd6c..6f245bd04a02b 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -80,7 +80,7 @@ export const initRoutes = ( ) => { registerFleetIntegrationsRoutes(router); registerLegacyRuleActionsRoutes(router, logger); - registerPrebuiltRulesRoutes(router, security); + registerPrebuiltRulesRoutes(router); registerRuleExceptionsRoutes(router); registerManageExceptionsRoutes(router); registerRuleManagementRoutes(router, config, ml, logger); @@ -99,19 +99,19 @@ export const initRoutes = ( registerResolverRoutes(router, getStartServices, config); - registerTimelineRoutes(router, config, security); + registerTimelineRoutes(router, config); // Detection Engine Signals routes that have the REST endpoints of /api/detection_engine/signals // POST /api/detection_engine/signals/status // Example usage can be found in security_solution/server/lib/detection_engine/scripts/signals - setSignalsStatusRoute(router, logger, telemetrySender, getStartServices); + setSignalsStatusRoute(router, logger, telemetrySender); setAlertTagsRoute(router); setAlertAssigneesRoute(router); querySignalsRoute(router, ruleDataClient); getSignalsMigrationStatusRoute(router); - createSignalsMigrationRoute(router, security); - finalizeSignalsMigrationRoute(router, ruleDataService, security); - deleteSignalsMigrationRoute(router, security); + createSignalsMigrationRoute(router); + finalizeSignalsMigrationRoute(router, ruleDataService); + deleteSignalsMigrationRoute(router); suggestUserProfilesRoute(router, getStartServices); // Detection Engine index routes that have the REST endpoints of /api/detection_engine/index @@ -130,14 +130,14 @@ export const initRoutes = ( createStoredScriptRoute(router, logger); deleteStoredScriptRoute(router); readPrebuiltDevToolContentRoute(router); - createPrebuiltSavedObjectsRoute(router, logger, security); - deletePrebuiltSavedObjectsRoute(router, security); + createPrebuiltSavedObjectsRoute(router, logger); + deletePrebuiltSavedObjectsRoute(router); getRiskScoreIndexStatusRoute(router); - installRiskScoresRoute(router, logger, security); + installRiskScoresRoute(router, logger); // Dashboards - registerDashboardsRoutes(router, logger, security); - registerTagsRoutes(router, logger, security); + registerDashboardsRoutes(router, logger); + registerTagsRoutes(router, logger); const { previewTelemetryUrlEnabled } = config.experimentalFeatures; if (previewTelemetryUrlEnabled) { // telemetry preview endpoint for e2e integration tests only at the moment. diff --git a/x-pack/plugins/security_solution_serverless/server/cloud_security/defend_for_containers_metering.ts b/x-pack/plugins/security_solution_serverless/server/cloud_security/defend_for_containers_metering.ts index 949eb709ec3a5..61240df94f9b2 100644 --- a/x-pack/plugins/security_solution_serverless/server/cloud_security/defend_for_containers_metering.ts +++ b/x-pack/plugins/security_solution_serverless/server/cloud_security/defend_for_containers_metering.ts @@ -76,7 +76,8 @@ export const getUsageRecords = async ( { range: { 'event.ingested': { - gt: searchFrom.toISOString(), + // gt: searchFrom.toISOString(), Tech debt: https://github.com/elastic/security-team/issues/9895 + gte: `now-30m`, }, }, }, diff --git a/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts b/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts index 994cbfe273304..0e47b982e692e 100644 --- a/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts +++ b/x-pack/plugins/security_solution_serverless/server/common/services/usage_reporting_service.ts @@ -19,11 +19,13 @@ export class UsageReportingService { records: UsageRecord[], url = USAGE_SERVICE_USAGE_URL ): Promise { + const isHttps = url.includes('https'); + return fetch(url, { method: 'post', body: JSON.stringify(records), headers: { 'Content-Type': 'application/json' }, - agent, + agent: isHttps ? agent : undefined, // Conditionally add agent if URL is HTTPS for supporting integration tests. }); } } diff --git a/x-pack/plugins/security_solution_serverless/server/config.ts b/x-pack/plugins/security_solution_serverless/server/config.ts index 60336c6f6e5f3..4b9700aaca6b2 100644 --- a/x-pack/plugins/security_solution_serverless/server/config.ts +++ b/x-pack/plugins/security_solution_serverless/server/config.ts @@ -17,11 +17,17 @@ export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: false }), productTypes, /** - * Usage Reporting: the interval between runs of the task + * Usage Reporting: the interval between runs of the endpoint task */ usageReportingTaskInterval: schema.string({ defaultValue: '5m' }), + /** + * Usage Reporting: the interval between runs of the cloud security task + */ + + cloudSecurityUsageReportingTaskInterval: schema.string({ defaultValue: '30m' }), + /** * Usage Reporting: timeout value for how long the task should run. */ diff --git a/x-pack/plugins/security_solution_serverless/server/plugin.ts b/x-pack/plugins/security_solution_serverless/server/plugin.ts index d63152f169490..f81a8e013e290 100644 --- a/x-pack/plugins/security_solution_serverless/server/plugin.ts +++ b/x-pack/plugins/security_solution_serverless/server/plugin.ts @@ -113,7 +113,7 @@ export class SecuritySolutionServerlessPlugin this.cloudSecurityUsageReportingTask ?.start({ taskManager: pluginsSetup.taskManager, - interval: cloudSecurityMetringTaskProperties.interval, + interval: this.config.cloudSecurityUsageReportingTaskInterval, }) .catch(() => {}); diff --git a/x-pack/plugins/stack_alerts/kibana.jsonc b/x-pack/plugins/stack_alerts/kibana.jsonc index 9f2f33abf1f6e..4d000228b0e07 100644 --- a/x-pack/plugins/stack_alerts/kibana.jsonc +++ b/x-pack/plugins/stack_alerts/kibana.jsonc @@ -22,7 +22,7 @@ ], "requiredBundles": [ "esUiShared", - "textBasedLanguages" + "esql" ], "extraPublicDirs": ["common"] } diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx index 3ff2b70522e9a..e9ceabf4639b2 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx @@ -17,7 +17,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { getFields, RuleTypeParamsExpressionProps } from '@kbn/triggers-actions-ui-plugin/public'; -import { TextBasedLangEditor } from '@kbn/text-based-languages/public'; +import { TextBasedLangEditor } from '@kbn/esql/public'; import { fetchFieldsFromESQL } from '@kbn/text-based-editor'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; import type { AggregateQuery } from '@kbn/es-query'; diff --git a/x-pack/plugins/stack_alerts/tsconfig.json b/x-pack/plugins/stack_alerts/tsconfig.json index 081b0588d39aa..f187bf466e375 100644 --- a/x-pack/plugins/stack_alerts/tsconfig.json +++ b/x-pack/plugins/stack_alerts/tsconfig.json @@ -38,7 +38,7 @@ "@kbn/discover-plugin", "@kbn/rule-data-utils", "@kbn/alerts-as-data-utils", - "@kbn/text-based-languages", + "@kbn/esql", "@kbn/text-based-editor", "@kbn/expressions-plugin", "@kbn/core-http-browser", diff --git a/x-pack/plugins/stack_connectors/common/sentinelone/schema.ts b/x-pack/plugins/stack_connectors/common/sentinelone/schema.ts index 66a50a42dc419..80f86a5c05ad8 100644 --- a/x-pack/plugins/stack_connectors/common/sentinelone/schema.ts +++ b/x-pack/plugins/stack_connectors/common/sentinelone/schema.ts @@ -162,7 +162,10 @@ export const SentinelOneIsolateHostResponseSchema = schema.object({ export const SentinelOneGetRemoteScriptsParamsSchema = schema.object({ query: schema.nullable(schema.string()), + // Possible values (multiples comma delimiter): `linux` or `macos` or `windows` osTypes: schema.nullable(schema.string()), + // possible values (multiples comma delimiter): `action` or `artifactCollection` or `dataCollection` + scriptType: schema.nullable(schema.string()), }); export const SentinelOneFetchAgentFilesParamsSchema = schema.object({ diff --git a/x-pack/plugins/stack_connectors/public/connector_types/cases_webhook/webhook.tsx b/x-pack/plugins/stack_connectors/public/connector_types/cases_webhook/webhook.tsx index 6e86df15c09c7..c547553260813 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/cases_webhook/webhook.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/cases_webhook/webhook.tsx @@ -27,7 +27,6 @@ export function getConnectorType(): ConnectorTypeModel< defaultMessage: 'Send a request to a Case Management web service.', } ), - isExperimental: true, actionTypeTitle: i18n.translate( 'xpack.stackConnectors.components.casesWebhookxpack.stackConnectors.components.casesWebhook.connectorTypeTitle', { diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index a3ef8accb2b32..d696f1ad07208 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -2525,8 +2525,8 @@ "unifiedDocViewer.sourceViewer.errorMessage": "Impossible de récupérer les données pour le moment. Actualisez l'onglet et réessayez.", "unifiedDocViewer.sourceViewer.errorMessageTitle": "Une erreur s'est produite.", "unifiedDocViewer.sourceViewer.refresh": "Actualiser", - "textBasedLanguages.advancedSettings.enableESQL.discussLinkText": "discuss.elastic.co/c/elastic-stack/kibana", - "textBasedLanguages.advancedSettings.enableESQLTitle": "Activer ES|QL", + "esql.advancedSettings.enableESQL.discussLinkText": "discuss.elastic.co/c/elastic-stack/kibana", + "esql.advancedSettings.enableESQLTitle": "Activer ES|QL", "domDragDrop.announce.cancelled": "Mouvement annulé. {label} revenu à sa position initiale", "domDragDrop.announce.cancelledItem": "Mouvement annulé. {label} revenu au groupe {groupLabel} à la position {position}", "domDragDrop.announce.dropped.combineCompatible": "Combinaisons de {label} dans le {groupLabel} vers {dropLabel} dans le groupe {dropGroupLabel} à la position {dropPosition} dans le calque {dropLayerNumber}", @@ -13310,7 +13310,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBasedSettings.knowledgeBaseDescription": "Pour commencer, configurez ELSER dans {machineLearning}. {seeDocs}", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "Initialisé sur `{kbIndexPattern}`", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel": "Envoyez à l'Assistant d'IA des informations sur vos {alertsCount} alertes ouvertes ou confirmées les plus récentes et les plus risquées.", - "xpack.elasticAssistant.assistant.technicalPreview.tooltipContent": "Les réponses des systèmes d'IA ne sont pas toujours tout à fait exactes. Pour en savoir plus sur la fonctionnalité d'assistant et son utilisation, consultez {documentationLink}.", "xpack.elasticAssistant.dataAnonymizationEditor.contextEditor.selectAllFields": "Sélectionnez l'ensemble des {totalFields} champs", "xpack.elasticAssistant.dataAnonymizationEditor.contextEditor.selectedFields": "{selected} champs sélectionnés", "xpack.elasticAssistant.dataAnonymizationEditor.stats.allowedStat.allowedTooltip": "{allowed} champs sur {total} dans ce contexte sont autorisés à être inclus dans la conversation", @@ -13511,9 +13510,6 @@ "xpack.elasticAssistant.knowledgeBase.setupError": "Erreur lors de la configuration de la base de connaissances", "xpack.elasticAssistant.knowledgeBase.statusError": "Erreur lors de la récupération du statut de la base de connaissances", "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.enterprisePrompt": "L'assistant d'IA d'Elastic n'est accessible qu'aux entreprises. Veuillez mettre votre licence à niveau pour bénéficier de cette fonctionnalité.", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral2Prompt": "Avant toute chose, il faut configurer un Connecteur d'intelligence artificielle générative pour lancer cette expérience de chat ! Avec le connecteur d'IA générative, vous pourrez configurer l'accès à un service OpenAI ou à un service Amazon Bedrock, mais sachez que vous serez en mesure de déployer vos propres modèles au sein d'une instance Elastic Cloud et de les y utiliser à l'avenir... 😉", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral3Prompt": "Cliquez sur le bouton \"Ajouter un connecteur\" ci-dessous pour continuer la conversation.", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneralPrompt": "Bienvenue sur votre assistant d’intelligence artificielle Elastic. Je suis votre portail 100 % open-code vers votre vie Elastic. Avec le temps, je serai capable de répondre à vos questions et de vous apporter mon aide concernant l’ensemble de vos informations contenues dans Elastic, et bien plus encore. En attendant, j’espère que cet aperçu anticipé vous donnera une idée de ce que nous pouvons créer en travaillant ensemble, en toute transparence. À bientôt !", "xpack.embeddableEnhanced.actions.panelNotifications.manyDrilldowns": "Le panneau comporte {count} recherches", "xpack.embeddableEnhanced.actions.panelNotifications.oneDrilldown": "Le panneau comporte 1 recherche", "xpack.embeddableEnhanced.Drilldowns": "Explorations", @@ -17672,7 +17668,6 @@ "xpack.fleet.agentLogs.downloadLink": "télécharger", "xpack.fleet.agentLogs.logDisabledCallOutTitle": "La collecte de logs est désactivée", "xpack.fleet.agentLogs.logLevelSelectText": "Niveau du log", - "xpack.fleet.agentLogs.openInDiscoverUiLinkText": "Ouvrir dans Discover", "xpack.fleet.agentLogs.openInLogsUiLinkText": "Ouvrir dans Logs", "xpack.fleet.agentLogs.searchPlaceholderText": "Rechercher dans les logs…", "xpack.fleet.agentLogs.selectLogLevel.errorTitleText": "Erreur lors de la mise à jour du niveau de logging de l'agent", @@ -23425,7 +23420,7 @@ "xpack.lens.shared.axisNameLabel": "Titre de l'axe", "xpack.lens.shared.chartValueLabelVisibilityLabel": "Étiquettes", "xpack.lens.shared.chartValueLabelVisibilityTooltip": "Si l'espace est insuffisant, les étiquettes de valeurs pourront être masquées", - "xpack.lens.shared.curveLabel": "Options visuelles", + "xpack.lens.shared.visualOptionsLabel": "Options visuelles", "xpack.lens.shared.legendAlignmentLabel": "Alignement", "xpack.lens.shared.legendInsideColumnsLabel": "Nombre de colonnes", "xpack.lens.shared.legendInsideLocationAlignmentLabel": "Alignement", @@ -32720,7 +32715,6 @@ "xpack.securitySolution.endpointResponseActions.actionError.errorMessage": "{ errorCount, plural, =1 {Erreur rencontrée} other {Erreurs rencontrées}} :", "xpack.securitySolution.enrichment.noInvestigationEnrichment": "Aucun renseignement supplémentaire sur les menaces n'a été détecté sur la période sélectionnée. Sélectionnez une autre plage temporelle ou {link} afin de collecter des renseignements sur les menaces pour les détecter et les comparer.", "xpack.securitySolution.entityAnalytics.anomalies.moduleNotCompatibleTitle": "{incompatibleJobCount} {incompatibleJobCount, plural, =1 {tâche est actuellement indisponible} other {tâches sont actuellement indisponibles}}", - "xpack.securitySolution.entityAnalytics.riskDashboard.learnMore": "Découvrez plus d'informations sur les risques {riskEntity}", "xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle": "Nom de {riskEntity}", "xpack.securitySolution.entityAnalytics.riskDashboard.riskLevelTitle": "Score de risque de {riskEntity}", "xpack.securitySolution.entityAnalytics.riskEngine.missingClusterPrivilege": "Privilèges de cluster manquants : {privileges}.", @@ -37051,7 +37045,6 @@ "xpack.securitySolution.riskInformation.howOftenTitle": "À quelle fréquence le risque est-il calculé ?", "xpack.securitySolution.riskInformation.informationAriaLabel": "Informations", "xpack.securitySolution.riskInformation.introText": "L'analyse de risque des entités détecte les hôtes et les utilisateurs risqués à l'intérieur de votre environnement.", - "xpack.securitySolution.riskInformation.learnMore": "Découvrez plus d'informations sur les risques des entités", "xpack.securitySolution.riskInformation.levelHeader": "Niveau de risque", "xpack.securitySolution.riskInformation.riskCalculationStep1": "Uniquement les utilisateurs et les hôtes (entités) de scores associés aux alertes de détection non fermées.", "xpack.securitySolution.riskInformation.riskCalculationStep2": "Génère un score de catégorie \"Alerte\" en regroupant les alertes par identificateur d'entité, afin que les alertes ayant des scores de risque plus élevés contribuent davantage que les alertes dont les scores de risque sont moins élevés.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 69c0d2396ec0e..5ada4e124f4c7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2522,8 +2522,8 @@ "unifiedDocViewer.sourceViewer.errorMessage": "現在データを取得できませんでした。タブを更新して、再試行してください。", "unifiedDocViewer.sourceViewer.errorMessageTitle": "エラーが発生しました", "unifiedDocViewer.sourceViewer.refresh": "更新", - "textBasedLanguages.advancedSettings.enableESQL.discussLinkText": "discuss.elastic.co/c/elastic-stack/kibana", - "textBasedLanguages.advancedSettings.enableESQLTitle": "ES|QLを有効化", + "esql.advancedSettings.enableESQL.discussLinkText": "discuss.elastic.co/c/elastic-stack/kibana", + "esql.advancedSettings.enableESQLTitle": "ES|QLを有効化", "domDragDrop.announce.cancelled": "移動がキャンセルされました。{label}は初期位置に戻りました", "domDragDrop.announce.cancelledItem": "移動がキャンセルされました。{label}は位置{position}の{groupLabel}グループに戻りました", "domDragDrop.announce.dropped.combineCompatible": "レイヤー{dropLayerNumber}の位置{dropPosition}で、グループ{groupLabel}の{label}をグループ{dropGroupLabel}の{dropLabel}と結合しました", @@ -13289,7 +13289,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBasedSettings.knowledgeBaseDescription": "{machineLearning}内でELSERを構成して開始します。{seeDocs}", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "`{kbIndexPattern}`に初期化されました", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel": "{alertsCount}件の最新の最もリスクが高い未解決または確認済みのアラートに関する情報をAI Assistantに送信します。", - "xpack.elasticAssistant.assistant.technicalPreview.tooltipContent": "AIシステムからの応答は、必ずしも完全に正確であるとは限りません。アシスタント機能とその使用方法の詳細については、{documentationLink}を参照してください。", "xpack.elasticAssistant.dataAnonymizationEditor.contextEditor.selectAllFields": "すべての{totalFields}フィールドを選択", "xpack.elasticAssistant.dataAnonymizationEditor.contextEditor.selectedFields": "選択した{selected}フィールド", "xpack.elasticAssistant.dataAnonymizationEditor.stats.allowedStat.allowedTooltip": "このコンテキストの{total}フィールドのうち{allowed}個を会話に含めることができます", @@ -13490,9 +13489,6 @@ "xpack.elasticAssistant.knowledgeBase.setupError": "ナレッジベースの設定エラー", "xpack.elasticAssistant.knowledgeBase.statusError": "ナレッジベースステータスの取得エラー", "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.enterprisePrompt": "Elastic AI Assistantはエンタープライズユーザーのみご利用いただけます。この機能を使用するには、ライセンスをアップグレードしてください。", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral2Prompt": "まず最初に、このチャットエクスペリエンスを開始するために生成AIコネクターを設定する必要があります。生成AIコネクターを使用すると、OpenAI ServiceまたはAmazon Bedrockサービスへのアクセスを設定できます。しかし、将来的にはElastic Cloudインスタンス内に独自のモデルをデプロイして、それをここで使うことができるようになると考えてください... 😉", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral3Prompt": "会話を続けるには、以下の[コネクターの追加]ボタンをクリックしてください。", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneralPrompt": "Elastic AI Assistantへようこそ!Elasticを活用するための100%オープンコードのポータルです。いずれは、Elasticにあるすべての情報、そしてもっともっと多くのことについて、質問に答えたり、サポートを提供したりできるようになるでしょう。それまでは、この早期プレビューが、オープンな場で協力するときに生み出せるものの可能性を知るきっかけになることを願っています。どうぞよろしくお願いいたします。", "xpack.embeddableEnhanced.actions.panelNotifications.manyDrilldowns": "パネルには{count}個のドリルダウンがあります", "xpack.embeddableEnhanced.actions.panelNotifications.oneDrilldown": "パネルには 1 個のドリルダウンがあります", "xpack.embeddableEnhanced.Drilldowns": "ドリルダウン", @@ -17650,7 +17646,6 @@ "xpack.fleet.agentLogs.downloadLink": "ダウンロード", "xpack.fleet.agentLogs.logDisabledCallOutTitle": "ログ収集は無効です", "xpack.fleet.agentLogs.logLevelSelectText": "ログレベル", - "xpack.fleet.agentLogs.openInDiscoverUiLinkText": "Discoverで開く", "xpack.fleet.agentLogs.openInLogsUiLinkText": "ログで開く", "xpack.fleet.agentLogs.searchPlaceholderText": "ログを検索…", "xpack.fleet.agentLogs.selectLogLevel.errorTitleText": "エージェントログレベルの更新エラー", @@ -23406,7 +23401,7 @@ "xpack.lens.shared.axisNameLabel": "軸のタイトル", "xpack.lens.shared.chartValueLabelVisibilityLabel": "ラベル", "xpack.lens.shared.chartValueLabelVisibilityTooltip": "十分なスペースがない場合、値ラベルが非表示になることがあります。", - "xpack.lens.shared.curveLabel": "視覚オプション", + "xpack.lens.shared.visualOptionsLabel": "視覚オプション", "xpack.lens.shared.legendAlignmentLabel": "アラインメント", "xpack.lens.shared.legendInsideColumnsLabel": "列の数", "xpack.lens.shared.legendInsideLocationAlignmentLabel": "アラインメント", @@ -32693,7 +32688,6 @@ "xpack.securitySolution.endpointResponseActions.actionError.errorMessage": "次の{ errorCount, plural, other {件のエラー}}が発生しました:", "xpack.securitySolution.enrichment.noInvestigationEnrichment": "選択した期間内に追加の脅威情報が見つかりませんでした。別の時間枠、または{link}を試して、脅威の検出と照合のための脅威インテリジェンスを収集します。", "xpack.securitySolution.entityAnalytics.anomalies.moduleNotCompatibleTitle": "{incompatibleJobCount} {incompatibleJobCount, plural, other {件のジョブ}}が現在使用できません", - "xpack.securitySolution.entityAnalytics.riskDashboard.learnMore": "{riskEntity}リスクの詳細", "xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle": "{riskEntity}名", "xpack.securitySolution.entityAnalytics.riskDashboard.riskLevelTitle": "{riskEntity}リスクレベル", "xpack.securitySolution.entityAnalytics.riskEngine.missingClusterPrivilege": "クラスター権限が不足しています:{privileges}。", @@ -37026,7 +37020,6 @@ "xpack.securitySolution.riskInformation.howOftenTitle": "リスクを計算する頻度", "xpack.securitySolution.riskInformation.informationAriaLabel": "情報", "xpack.securitySolution.riskInformation.introText": "Entity Risk Analyticsは、環境内のリスクのあるホストとユーザーを明らかにします。", - "xpack.securitySolution.riskInformation.learnMore": "エンティティリスクの詳細", "xpack.securitySolution.riskInformation.levelHeader": "リスクレベル", "xpack.securitySolution.riskInformation.riskCalculationStep1": "クローズされていない検出アラートに関連付けられたユーザーとホスト(エンティティ)のみスコア付けします。", "xpack.securitySolution.riskInformation.riskCalculationStep2": "エンティティID別にアラートを集約することで、「アラート」カテゴリスコアを生成します。リスクスコアが高いアラートがリスクスコアが低いアラートよりも大きく寄与するように設定されます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 794c9258dc233..373b448116918 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2526,8 +2526,8 @@ "unifiedDocViewer.sourceViewer.errorMessage": "当前无法获取数据。请刷新选项卡以重试。", "unifiedDocViewer.sourceViewer.errorMessageTitle": "发生错误", "unifiedDocViewer.sourceViewer.refresh": "刷新", - "textBasedLanguages.advancedSettings.enableESQL.discussLinkText": "discuss.elastic.co/c/elastic-stack/kibana", - "textBasedLanguages.advancedSettings.enableESQLTitle": "启用 ES|QL", + "esql.advancedSettings.enableESQL.discussLinkText": "discuss.elastic.co/c/elastic-stack/kibana", + "esql.advancedSettings.enableESQLTitle": "启用 ES|QL", "domDragDrop.announce.cancelled": "移动已取消。{label} 将返回至其初始位置", "domDragDrop.announce.cancelledItem": "移动已取消。{label} 返回至 {groupLabel} 组中的位置 {position}", "domDragDrop.announce.dropped.combineCompatible": "已将组 {groupLabel} 中的 {label} 组合到图层 {dropLayerNumber} 的组 {dropGroupLabel} 中的位置 {dropPosition} 上的 {dropLabel}", @@ -13315,7 +13315,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBasedSettings.knowledgeBaseDescription": "在 {machineLearning} 中配置 ELSER 以开始。{seeDocs}", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "已初始化为 `{kbIndexPattern}`", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel": "发送有关 {alertsCount} 个最新和风险最高的未决或已确认告警的 AI 助手信息。", - "xpack.elasticAssistant.assistant.technicalPreview.tooltipContent": "来自 AI 系统的响应可能不会始终完全准确。有关辅助功能及其用法的详细信息,请参阅 {documentationLink}。", "xpack.elasticAssistant.dataAnonymizationEditor.contextEditor.selectAllFields": "选择所有 {totalFields} 个字段", "xpack.elasticAssistant.dataAnonymizationEditor.contextEditor.selectedFields": "已选定 {selected} 个字段", "xpack.elasticAssistant.dataAnonymizationEditor.stats.allowedStat.allowedTooltip": "允许在对话中包含此上下文中的 {allowed} 个(共 {total} 个)字段", @@ -13516,9 +13515,6 @@ "xpack.elasticAssistant.knowledgeBase.setupError": "设置知识库时出错", "xpack.elasticAssistant.knowledgeBase.statusError": "提取知识库状态时出错", "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.enterprisePrompt": "Elastic AI 助手仅对企业用户可用。请升级许可证以使用此功能。", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral2Prompt": "首先,我们需要设置生成式 AI 连接器以继续这种聊天体验!使用生成式 AI 连接器,您将能够配置 OpenAI 服务或 Amazon Bedrock 服务的访问权限,但请您相信,您将能够在 Elastic Cloud 实例中部署自己的模型,并于未来在此处使用那些模型……😉", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneral3Prompt": "接下来,单击下面的“添加连接器”按钮继续对话!", - "xpack.elasticAssistant.securityAssistant.content.prompts.welcome.welcomeGeneralPrompt": "欢迎使用 Elastic AI 助手!我是您的 100% 开放源代码门户,可帮助您熟练使用 Elastic。一段时间后,我将能够回答问题,并利用 Elastic 中的所有信息提供帮助等。到那时,我希望这个早期预览版本将为您打开思路,为我们的公开协作创造各种可能性。加油!", "xpack.embeddableEnhanced.actions.panelNotifications.manyDrilldowns": "面板有 {count} 个向下钻取", "xpack.embeddableEnhanced.actions.panelNotifications.oneDrilldown": "面板有 1 个向下钻取", "xpack.embeddableEnhanced.Drilldowns": "向下钻取", @@ -17679,7 +17675,6 @@ "xpack.fleet.agentLogs.downloadLink": "下载", "xpack.fleet.agentLogs.logDisabledCallOutTitle": "日志收集已禁用", "xpack.fleet.agentLogs.logLevelSelectText": "日志级别", - "xpack.fleet.agentLogs.openInDiscoverUiLinkText": "在 Discover 中打开", "xpack.fleet.agentLogs.openInLogsUiLinkText": "在日志中打开", "xpack.fleet.agentLogs.searchPlaceholderText": "搜索日志……", "xpack.fleet.agentLogs.selectLogLevel.errorTitleText": "更新代理日志记录级别时出错", @@ -23439,7 +23434,7 @@ "xpack.lens.shared.axisNameLabel": "轴标题", "xpack.lens.shared.chartValueLabelVisibilityLabel": "标签", "xpack.lens.shared.chartValueLabelVisibilityTooltip": "如果没有足够的空间,可能会隐藏值标签", - "xpack.lens.shared.curveLabel": "视觉选项", + "xpack.lens.shared.visualOptionsLabel": "视觉选项", "xpack.lens.shared.legendAlignmentLabel": "对齐方式", "xpack.lens.shared.legendInsideColumnsLabel": "列数目", "xpack.lens.shared.legendInsideLocationAlignmentLabel": "对齐方式", @@ -32737,7 +32732,6 @@ "xpack.securitySolution.endpointResponseActions.actionError.errorMessage": "遇到以下{ errorCount, plural, other {错误}}:", "xpack.securitySolution.enrichment.noInvestigationEnrichment": "在选定时间范围内未发现其他威胁情报。请尝试不同时间范围,或 {link} 以收集威胁情报用于威胁检测和匹配。", "xpack.securitySolution.entityAnalytics.anomalies.moduleNotCompatibleTitle": "{incompatibleJobCount} 个{incompatibleJobCount, plural, other {作业}}当前不可用", - "xpack.securitySolution.entityAnalytics.riskDashboard.learnMore": "详细了解 {riskEntity} 风险", "xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle": "{riskEntity}名称", "xpack.securitySolution.entityAnalytics.riskDashboard.riskLevelTitle": "{riskEntity} 风险级别", "xpack.securitySolution.entityAnalytics.riskEngine.missingClusterPrivilege": "缺少集群权限:{privileges}。", @@ -37069,7 +37063,6 @@ "xpack.securitySolution.riskInformation.howOftenTitle": "多久计算一次风险?", "xpack.securitySolution.riskInformation.informationAriaLabel": "信息", "xpack.securitySolution.riskInformation.introText": "实体风险分析将显示您的环境中存在的有风险主机和用户。", - "xpack.securitySolution.riskInformation.learnMore": "详细了解实体风险", "xpack.securitySolution.riskInformation.levelHeader": "风险级别", "xpack.securitySolution.riskInformation.riskCalculationStep1": "仅对与尚未关闭的检测告警关联的用户和主机(实体)评分。", "xpack.securitySolution.riskInformation.riskCalculationStep2": "通过按实体标识符聚合告警来生成“告警”类别分数,从而使风险分数高的告警贡献高于风险分数低的告警。", diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts b/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts index 00bcffd553af1..77dd859bed4d9 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts @@ -145,6 +145,76 @@ export async function createPackagePolicy( return postPackageResponse.item; } +export async function createCloudDefendPackagePolicy( + supertest: SuperTestAgent, + agentPolicyId: string, + roleAuthc?: RoleCredentials, + internalRequestHeader?: { 'x-elastic-internal-origin': string; 'kbn-xsrf': string } +) { + const version = '1.2.5'; + const installationPayload = { + policy_id: agentPolicyId, + package: { + name: 'cloud_defend', + version, + }, + name: 'cloud_defend-1', + description: '', + namespace: 'default', + inputs: { + 'cloud_defend-cloud_defend/control': { + enabled: true, + vars: { + configuration: + 'process:\n selectors:\n - name: allProcesses\n operation: [fork, exec]\n responses:\n - match: [allProcesses]\n actions: [log]\nfile:\n selectors:\n - name: executableChanges\n operation: [createExecutable, modifyExecutable]\n responses:\n - match: [executableChanges]\n actions: [alert]\n', + }, + streams: { + 'cloud_defend.alerts': { + enabled: true, + }, + 'cloud_defend.file': { + enabled: true, + }, + 'cloud_defend.heartbeat': { + enabled: true, + vars: { + period: '30m', + }, + }, + 'cloud_defend.metrics': { + enabled: true, + vars: { + period: '24h', + }, + }, + 'cloud_defend.process': { + enabled: true, + }, + }, + }, + }, + force: true, + }; + + const { body: postPackageResponse } = + roleAuthc && internalRequestHeader + ? await supertest + .post(`/api/fleet/package_policies`) + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(internalRequestHeader) + .set(roleAuthc.apiKeyHeader) + .send(installationPayload) + .expect(200) + : await supertest + .post(`/api/fleet/package_policies`) + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set('kbn-xsrf', 'xxxx') + .send(installationPayload) + .expect(200); + + return postPackageResponse.item; +} + export const createUser = async (security: SecurityService, userName: string, roleName: string) => { await security.user.create(userName, { password: 'changeme', diff --git a/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts b/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts index ecfcff804d69a..f5d67ba06bc15 100644 --- a/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts +++ b/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; const API_BASE_PATH = '/api/index_management'; @@ -17,46 +18,48 @@ export default function ({ getService }: FtrProviderContext) { const inferenceId = 'my-elser-model'; const taskType = 'sparse_embedding'; const service = 'elser'; + const modelId = '.elser_model_2'; describe('Inference endpoints', function () { - before(async () => { - log.debug(`Creating inference endpoint`); - try { - await ml.api.createInferenceEndpoint(inferenceId, taskType, { - service, - service_settings: { - num_allocations: 1, - num_threads: 1, - }, - }); - } catch (err) { - log.debug('[Setup error] Error creating inference endpoint'); - throw err; - } - }); - after(async () => { - // Cleanup inference endpoints created for testing purposes try { - log.debug(`Deleting inference endpoint`); - await ml.api.deleteInferenceEndpoint(inferenceId, taskType); + log.debug(`Deleting underlying trained model`); + await ml.api.deleteTrainedModelES(modelId); + await ml.testResources.cleanMLSavedObjects(); } catch (err) { - log.debug('[Cleanup error] Error deleting inference endpoint'); + log.debug('[Cleanup error] Error deleting trained model or saved ml objects'); throw err; } }); - - describe('get inference endpoints', () => { - it('returns the existing inference endpoints', async () => { - const { body: inferenceEndpoints } = await supertest - .get(`${API_BASE_PATH}/inference/all`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') - .expect(200); - - expect(inferenceEndpoints).to.be.ok(); - expect(inferenceEndpoints[0].model_id).to.eql(inferenceId); + it('create inference endpoint', async () => { + log.debug(`create inference endpoint`); + await ml.api.createInferenceEndpoint(inferenceId, taskType, { + service, + service_settings: { + num_allocations: 1, + num_threads: 1, + model_id: modelId, + }, }); }); + it('get all inference endpoints and confirm inference endpoint exist', async () => { + const { body: inferenceEndpoints } = await supertest + .get(`${API_BASE_PATH}/inference/all`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + expect(inferenceEndpoints).to.be.ok(); + expect( + inferenceEndpoints.some( + (endpoint: InferenceAPIConfigResponse) => endpoint.model_id === inferenceId + ) + ).to.be(true); + }); + it('can delete inference endpoint', async () => { + log.debug(`Deleting inference endpoint`); + await ml.api.deleteInferenceEndpoint(inferenceId, taskType); + log.debug('> Inference endpoint deleted'); + }); }); } diff --git a/x-pack/test/api_integration/apis/ml/trained_models/index.ts b/x-pack/test/api_integration/apis/ml/trained_models/index.ts index 1bb92dca1ec56..c9bf98545e2b4 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/index.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/index.ts @@ -15,5 +15,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./delete_model')); loadTestFile(require.resolve('./put_model')); loadTestFile(require.resolve('./start_stop_deployment')); + loadTestFile(require.resolve('./model_downloads')); }); } diff --git a/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts new file mode 100644 index 0000000000000..7954a7b31e900 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts @@ -0,0 +1,136 @@ +/* + * 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 expect from '@kbn/expect'; +import { type NodesInfoNodeInfo } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { getCommonRequestHeader } from '../../../../functional/services/ml/common_api'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + const deployment = getService('deployment'); + const esSupertest = getService('esSupertest'); + + describe('GET trained_models/model_downloads', function () { + before(async () => { + await ml.api.initSavedObjects(); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('returns the list of models available for download', async () => { + const isCloud = await deployment.isCloud(); + + const { body: mlNodesResponse } = await esSupertest.get('/_nodes/ml:true/os'); + + // Check that all ML nodes are Intel-based. + const areMlNodesIntelBased = Object.values( + mlNodesResponse.nodes as Record + ).every((node) => node.os?.name === 'Linux' && node.os?.arch === 'amd64'); + + const isIntelBased = isCloud || areMlNodesIntelBased; + + const { body, status } = await supertest + .get(`/internal/ml/trained_models/model_downloads`) + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(getCommonRequestHeader('1')); + ml.api.assertResponseStatusCode(200, status, body); + + expect(body.length).to.eql(5); + + expect(body).to.eql([ + { + modelName: 'elser', + hidden: true, + version: 1, + config: { + input: { + field_names: ['text_field'], + }, + }, + description: 'Elastic Learned Sparse EncodeR v1 (Tech Preview)', + type: ['elastic', 'pytorch', 'text_expansion'], + model_id: '.elser_model_1', + }, + { + modelName: 'elser', + version: 2, + config: { + input: { + field_names: ['text_field'], + }, + }, + description: 'Elastic Learned Sparse EncodeR v2', + type: ['elastic', 'pytorch', 'text_expansion'], + model_id: '.elser_model_2', + ...(isIntelBased ? { default: true } : { recommended: true }), + }, + { + modelName: 'elser', + version: 2, + os: 'Linux', + arch: 'amd64', + config: { + input: { + field_names: ['text_field'], + }, + }, + description: 'Elastic Learned Sparse EncodeR v2, optimized for linux-x86_64', + type: ['elastic', 'pytorch', 'text_expansion'], + model_id: '.elser_model_2_linux-x86_64', + ...(isIntelBased ? { recommended: true } : {}), + }, + { + modelName: 'e5', + version: 1, + config: { + input: { + field_names: ['text_field'], + }, + }, + description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', + license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', + type: ['pytorch', 'text_embedding'], + model_id: '.multilingual-e5-small', + ...(isIntelBased ? { default: true } : { recommended: true }), + }, + { + modelName: 'e5', + version: 1, + os: 'Linux', + arch: 'amd64', + config: { + input: { + field_names: ['text_field'], + }, + }, + description: + 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', + license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', + type: ['pytorch', 'text_embedding'], + model_id: '.multilingual-e5-small_linux-x86_64', + ...(isIntelBased ? { recommended: true } : {}), + }, + ]); + }); + + it('returns an error for unauthorized user', async () => { + const { body, status } = await supertest + .get(`/internal/ml/trained_models/model_downloads`) + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(getCommonRequestHeader('1')); + ml.api.assertResponseStatusCode(403, status, body); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json b/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json index 74abc5de82dbf..ccfb82f5212a8 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json +++ b/x-pack/test/api_integration/apis/monitoring/apm/fixtures/instance.json @@ -872,7 +872,7 @@ "metricAgg": "max", "label": "Count", "title": "Requests Agent Configuration Management", - "description": "HTTP Requests received by agent configuration managemen", + "description": "HTTP Requests received by agent configuration management", "units": "/s", "format": "0,0.[00]", "hasCalculation": false, diff --git a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts index 45a95db642d2f..8b72afc194e35 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts @@ -103,8 +103,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { after(() => apmSynthtraceEsClient.clean()); - // FLAKY: https://github.com/elastic/kibana/issues/176948 - describe.skip('create rule without kql filter', () => { + describe('create rule without kql filter', () => { let ruleId: string; let alerts: ApmAlertFields[]; let actionId: string; @@ -175,10 +174,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { environment: 'production', interval: '1 hr', reason: - 'Error count is 30 in the last 1 hr for service: opbeans-php, env: production, name: tx-php, error key: c85df8159a74b47b461d6ddaa6ba7da38cfc3e74019aef66257d10df74adeb99, error name: a php error. Alert when > 1.', + 'Error count is 30 in the last 1 hr for service: opbeans-php, env: production, name: tx-php, error key: 000000000000000000000a php error, error name: a php error. Alert when > 1.', serviceName: 'opbeans-php', transactionName: 'tx-php', - errorGroupingKey: 'c85df8159a74b47b461d6ddaa6ba7da38cfc3e74019aef66257d10df74adeb99', + errorGroupingKey: '000000000000000000000a php error', errorGroupingName: 'a php error', threshold: '1', triggerValue: '30', @@ -192,8 +191,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { const alertReasons = [alerts[0]['kibana.alert.reason'], alerts[1]['kibana.alert.reason']]; expect(alertReasons).to.eql([ - 'Error count is 30 in the last 1 hr for service: opbeans-php, env: production, name: tx-php, error key: c85df8159a74b47b461d6ddaa6ba7da38cfc3e74019aef66257d10df74adeb99, error name: a php error. Alert when > 1.', - 'Error count is 15 in the last 1 hr for service: opbeans-java, env: production, name: tx-java, error key: b6a4ac83620b34ae44dd98a13e144782f88698f827af7edb10690c5e6e7d8597, error name: a java error. Alert when > 1.', + 'Error count is 30 in the last 1 hr for service: opbeans-php, env: production, name: tx-php, error key: 000000000000000000000a php error, error name: a php error. Alert when > 1.', + 'Error count is 15 in the last 1 hr for service: opbeans-java, env: production, name: tx-java, error key: 00000000000000000000a java error, error name: a java error. Alert when > 1.', ]); }); @@ -252,8 +251,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/176964 - describe.skip('create rule with kql filter for opbeans-php', () => { + describe('create rule with kql filter for opbeans-php', () => { let ruleId: string; before(async () => { diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts index 7d4ec54fa52fb..0d7460ff5be50 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts @@ -75,8 +75,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { await apmSynthtraceEsClient.clean(); }); - // FLAKY: https://github.com/elastic/kibana/issues/176996 - describe.skip('create rule for opbeans-java without kql filter', () => { + describe('create rule for opbeans-java without kql filter', () => { let ruleId: string; let actionId: string; let alerts: ApmAlertFields[]; @@ -188,8 +187,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/177061 - describe.skip('create rule for opbeans-node using kql filter', () => { + describe('create rule for opbeans-node using kql filter', () => { let ruleId: string; let alerts: ApmAlertFields[]; diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts index 6416337c3bfc9..509d839ecef6d 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts @@ -73,7 +73,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { await apmSynthtraceEsClient.clean(); }); - // FLAKY: https://github.com/elastic/kibana/issues/177104 describe('create rule without kql query', () => { let ruleId: string; let actionId: string; @@ -197,7 +196,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/177108 describe('create rule with kql query', () => { let ruleId: string; let alerts: ApmAlertFields[]; diff --git a/x-pack/test/apm_api_integration/tests/entities/services/services_entities_detailed_statistics.spec.ts b/x-pack/test/apm_api_integration/tests/entities/services/services_entities_detailed_statistics.spec.ts new file mode 100644 index 0000000000000..9e4cc209c6b92 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/entities/services/services_entities_detailed_statistics.spec.ts @@ -0,0 +1,191 @@ +/* + * 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 expect from '@kbn/expect'; +import { + APIClientRequestParamsOf, + APIReturnType, +} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; +import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; +import { apm, log, timerange } from '@kbn/apm-synthtrace-client'; +import { uniq, map } from 'lodash'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +type ServicesEntitiesDetailedStatisticsReturn = + APIReturnType<'POST /internal/apm/entities/services/detailed_statistics'>; + +export default function ApiTest({ getService }: FtrProviderContext) { + const registry = getService('registry'); + + const apmApiClient = getService('apmApiClient'); + const synthtrace = getService('apmSynthtraceEsClient'); + const logSynthtrace = getService('logSynthtraceEsClient'); + + const start = '2024-01-01T00:00:00.000Z'; + const end = '2024-01-01T00:59:59.999Z'; + + const serviceNames = ['my-service', 'synth-go']; + const hostName = 'synth-host'; + const serviceName = 'synth-go'; + + const EXPECTED_TPM = 5; + const EXPECTED_LATENCY = 1000; + const EXPECTED_FAILURE_RATE = 0.25; + const EXPECTED_LOG_RATE = 0.016666666666666666; + const EXPECTED_LOG_ERROR_RATE = 1; + + async function getStats( + overrides?: Partial< + APIClientRequestParamsOf<'POST /internal/apm/entities/services/detailed_statistics'>['params']['query'] + > + ) { + const response = await apmApiClient.readUser({ + endpoint: `POST /internal/apm/entities/services/detailed_statistics`, + params: { + query: { + start, + end, + environment: 'ENVIRONMENT_ALL', + kuery: '', + probability: 1, + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, + bucketSizeInSeconds: 60, + ...overrides, + }, + body: { + serviceNames: JSON.stringify(serviceNames), + }, + }, + }); + + return response.body; + } + + registry.when( + 'Services entities detailed statistics when data is generated', + { config: 'basic', archives: [] }, + () => { + let servicesEntitiesDetailedStatistics: ServicesEntitiesDetailedStatisticsReturn; + + const instance = apm.service('my-service', 'production', 'java').instance('instance'); + + before(async () => { + const interval = timerange(new Date(start).getTime(), new Date(end).getTime() - 1).interval( + '1m' + ); + + await logSynthtrace.index([ + timerange(start, end) + .interval('1m') + .rate(1) + .generator((timestamp) => + log + .create() + .message('This is a log message') + .logLevel('error') + .timestamp(timestamp) + .defaults({ + 'log.file.path': '/my-service.log', + 'service.name': serviceName, + 'host.name': hostName, + 'service.environment': 'test', + }) + ), + timerange(start, end) + .interval('2m') + .rate(1) + .generator((timestamp) => + log + .create() + .message('This is an error log message') + .logLevel('error') + .timestamp(timestamp) + .defaults({ + 'log.file.path': '/my-service.log', + 'service.name': 'my-service', + 'host.name': hostName, + 'service.environment': 'production', + }) + ), + timerange(start, end) + .interval('5m') + .rate(1) + .generator((timestamp) => + log + .create() + .message('This is an info message') + .logLevel('info') + .timestamp(timestamp) + .defaults({ + 'log.file.path': '/my-service.log', + 'service.name': 'my-service', + 'host.name': hostName, + 'service.environment': 'production', + }) + ), + ]); + + await synthtrace.index([ + interval.rate(3).generator((timestamp) => { + return instance + .transaction('GET /api') + .duration(EXPECTED_LATENCY) + .outcome('success') + .timestamp(timestamp); + }), + interval.rate(1).generator((timestamp) => { + return instance + .transaction('GET /api') + .duration(EXPECTED_LATENCY) + .outcome('failure') + .timestamp(timestamp); + }), + interval.rate(1).generator((timestamp) => { + return instance + .transaction('GET /api') + .duration(EXPECTED_LATENCY) + .outcome('unknown') + .timestamp(timestamp); + }), + ]); + }); + + after(() => synthtrace.clean()); + + describe('and transaction metrics are used', () => { + before(async () => { + servicesEntitiesDetailedStatistics = await getStats(); + }); + + it('returns the expected statistics for apm', () => { + const stats = servicesEntitiesDetailedStatistics.currentPeriod.apm['my-service']; + + expect(stats).not.empty(); + expect(uniq(map(stats.throughput, 'y'))).eql([EXPECTED_TPM], 'tpm'); + + expect(uniq(map(stats.latency, 'y'))).eql([EXPECTED_LATENCY * 1000], 'latency'); + + expect(uniq(map(stats.transactionErrorRate, 'y'))).eql( + [EXPECTED_FAILURE_RATE], + 'errorRate' + ); + }); + + it('returns the expected statistics for logs', () => { + const statsLogErrorRate = + servicesEntitiesDetailedStatistics.currentPeriod.logErrorRate[serviceName]; + const statsLogRate = + servicesEntitiesDetailedStatistics.currentPeriod.logRate[serviceName]; + + expect(statsLogErrorRate.every(({ y }) => y === EXPECTED_LOG_ERROR_RATE)).to.be(true); + expect(statsLogRate.every(({ y }) => y === EXPECTED_LOG_RATE)).to.be(true); + }); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/environment/generate_data.ts b/x-pack/test/apm_api_integration/tests/environment/generate_data.ts index af71533bf3e0d..3cfd5d92e00f1 100644 --- a/x-pack/test/apm_api_integration/tests/environment/generate_data.ts +++ b/x-pack/test/apm_api_integration/tests/environment/generate_data.ts @@ -63,5 +63,5 @@ export async function generateData({ return [...loopGeneratedDocs, customDoc]; }); - await apmSynthtraceEsClient.index(docs); + return apmSynthtraceEsClient.index(docs); } diff --git a/x-pack/test/apm_api_integration/tests/environment/get_environment.spec.ts b/x-pack/test/apm_api_integration/tests/environment/get_environment.spec.ts index 8a717e735b0f2..e67c601e7713c 100644 --- a/x-pack/test/apm_api_integration/tests/environment/get_environment.spec.ts +++ b/x-pack/test/apm_api_integration/tests/environment/get_environment.spec.ts @@ -19,15 +19,14 @@ export default function environmentsAPITests({ getService }: FtrProviderContext) const apmApiClient = getService('apmApiClient'); const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - // FLAKY: https://github.com/elastic/kibana/issues/177305 registry.when('environments when data is loaded', { config: 'basic', archives: [] }, async () => { - before(async () => { - await generateData({ + before(async () => + generateData({ apmSynthtraceEsClient, start: startNumber, end: endNumber, - }); - }); + }) + ); after(() => apmSynthtraceEsClient.clean()); diff --git a/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts b/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts index aa2887a04da21..8f2a2fa2693d4 100644 --- a/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts +++ b/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts @@ -186,16 +186,13 @@ export default function ({ getService }: FtrProviderContext) { describe('Vulnerability Dashboard API', async () => { beforeEach(async () => { + await index.removeFindings(); + await index.removeScores(); await waitForPluginInitialized(); await index.addScores(scoresVulnerabilitiesMock); await index.addFindings(vulnerabilitiesLatestMock); }); - afterEach(async () => { - await index.removeFindings(); - await index.removeScores(); - }); - it('responds with a 200 status code and matching data mock', async () => { const { body } = await supertest .get(`/internal/cloud_security_posture/vulnerabilities_dashboard`) diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/basic_ui_sanity.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/basic_ui_sanity.ts deleted file mode 100644 index 495105017e5c2..0000000000000 --- a/x-pack/test/cloud_security_posture_functional/cloud_tests/basic_ui_sanity.ts +++ /dev/null @@ -1,75 +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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ getPageObjects, getService }: FtrProviderContext) => { - const retry = getService('retry'); - const pageObjects = getPageObjects(['common', 'cloudPostureDashboard', 'header']); - - describe('Cloud Posture Dashboard Page', function () { - this.tags(['cloud_security_posture_ui_sanity']); - let cspDashboard: typeof pageObjects.cloudPostureDashboard; - let dashboard: typeof pageObjects.cloudPostureDashboard.dashboard; - - before(async () => { - cspDashboard = pageObjects.cloudPostureDashboard; - dashboard = pageObjects.cloudPostureDashboard.dashboard; - await cspDashboard.waitForPluginInitialized(); - await cspDashboard.navigateToComplianceDashboardPage(); - await retry.waitFor( - 'Cloud posture integration dashboard to be displayed', - async () => !!dashboard.getIntegrationDashboardContainer() - ); - }); - - describe('Cloud Dashboard', () => { - it('displays compliance score greater than 40', async () => { - await pageObjects.header.waitUntilLoadingHasFinished(); - const scoreElement = await dashboard.getCloudComplianceScore(); - const score = parseInt((await scoreElement.getVisibleText()).replace('%', ''), 10); - expect(score).to.be.greaterThan(40); - }); - - it('displays all compliance scores', async () => { - const scoresElements = await dashboard.getAllCloudComplianceScores(); - const scores: string[] = []; - for (const scoreElement of scoresElements) { - scores.push(await scoreElement.getVisibleText()); - } - // 3 scores for each cloud provider + 1 summary score - expect(scores.length).to.be(4); - }); - - it('displays a number of resources evaluated greater than 3000', async () => { - const resourcesEvaluated = await dashboard.getCloudResourcesEvaluated(); - const visibleText = await resourcesEvaluated.getVisibleText(); - const resourcesEvaluatedCount = parseInt(visibleText.replace(/,/g, ''), 10); - expect(resourcesEvaluatedCount).greaterThan(3000); - }); - }); - - describe('Kubernetes Dashboard', () => { - it('displays compliance score greater than 80', async () => { - await pageObjects.header.waitUntilLoadingHasFinished(); - const scoreElement = await dashboard.getKubernetesComplianceScore(); - const score = parseInt((await scoreElement.getVisibleText()).replace('%', ''), 10); - expect(score).to.be.greaterThan(80); - }); - - it('displays a number of resources evaluated greater than 150', async () => { - const resourcesEvaluated = await dashboard.getKubernetesResourcesEvaluated(); - const resourcesEvaluatedCount = parseInt( - (await resourcesEvaluated.getVisibleText()).replace(/,/g, ''), - 10 - ); - expect(resourcesEvaluatedCount).greaterThan(150); - }); - }); - }); -}; diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/benchmark_sanity.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/benchmark_sanity.ts new file mode 100644 index 0000000000000..277c1038e51ae --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/benchmark_sanity.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'benchmark']); + + describe('Benchmark Page - Sanity Tests', function () { + this.tags(['cloud_security_posture_ui_sanity']); + let benchmark: typeof pageObjects.benchmark; + + before(async () => { + benchmark = pageObjects.benchmark; + await benchmark.navigateToBenchnmarkPage(); + await benchmark.waitForPluginInitialized(); + }); + + it('Benchmark table exists', async () => { + expect(await benchmark.benchmarkPage.doesBenchmarkTableExists()); + }); + + it('Benchmarks count is more than 0', async () => { + const benchmarksRows = await benchmark.benchmarkPage.getBenchmarkTableRows(); + expect(benchmarksRows.length).to.be.greaterThan(0); + }); + + it('For each benchmark, evaluation and complience are not empty', async () => { + const benchmarksRows = await benchmark.benchmarkPage.getBenchmarkTableRows(); + for (const row of benchmarksRows) { + const benchmarkName = await benchmark.benchmarkPage.getCisNameCellData(row); + const evaluated = await benchmark.benchmarkPage.getEvaluatedCellData(row); + const compliance = await benchmark.benchmarkPage.getComplianceCellData(row); + expect(await evaluated).to.not.contain( + 'Add', + `The ${benchmarkName} does not have evaluated data` + ); + expect(await compliance).to.not.contain( + 'No', + `The ${benchmarkName} does not have compliance data` + ); + } + }); + }); +}; diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/dashboard_sanity.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/dashboard_sanity.ts new file mode 100644 index 0000000000000..6eaafcb154134 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/dashboard_sanity.ts @@ -0,0 +1,139 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const retry = getService('retry'); + const pageObjects = getPageObjects(['common', 'cloudPostureDashboard', 'header', 'findings']); + + describe('Cloud Posture Dashboard Page - Sanity Tests', function () { + this.tags(['cloud_security_posture_ui_sanity']); + let cspDashboard: typeof pageObjects.cloudPostureDashboard; + let dashboard: typeof pageObjects.cloudPostureDashboard.dashboard; + let findings: typeof pageObjects.findings; + let TAB_TYPES: typeof pageObjects.cloudPostureDashboard.TAB_TYPES; + + before(async () => { + cspDashboard = pageObjects.cloudPostureDashboard; + dashboard = pageObjects.cloudPostureDashboard.dashboard; + findings = pageObjects.findings; + TAB_TYPES = pageObjects.cloudPostureDashboard.TAB_TYPES; + await cspDashboard.waitForPluginInitialized(); + await cspDashboard.navigateToComplianceDashboardPage(); + await retry.waitFor( + 'Cloud posture integration dashboard to be displayed', + async () => !!dashboard.getIntegrationDashboardContainer() + ); + }); + + describe('Cloud Dashboard', () => { + it('displays compliance score greater than 40', async () => { + await pageObjects.header.waitUntilLoadingHasFinished(); + const scoreElement = await dashboard.getCloudComplianceScore(); + const score = parseInt((await scoreElement.getVisibleText()).replace('%', ''), 10); + expect(score).to.be.greaterThan(40); + }); + + it('displays all compliance scores', async () => { + const scoresElements = await dashboard.getAllCloudComplianceScores(); + const scores: string[] = []; + for (const scoreElement of scoresElements) { + scores.push(await scoreElement.getVisibleText()); + } + // 3 scores for each cloud provider + 1 summary score + expect(scores.length).to.be(4); + }); + + it('displays a number of resources evaluated greater than 1500', async () => { + const resourcesEvaluated = await dashboard.getCloudResourcesEvaluated(); + const visibleText = await resourcesEvaluated.getVisibleText(); + const resourcesEvaluatedCount = parseInt(visibleText.replace(/,/g, ''), 10); + expect(resourcesEvaluatedCount).greaterThan(1500); + }); + + it('Compliance By CIS sections have non empty values', async () => { + const complianceScoresChartPanel = await dashboard.getAllComplianceScoresByCisSection( + TAB_TYPES.CLOUD + ); + expect(complianceScoresChartPanel.length).to.be.greaterThan(0); + for (const score of complianceScoresChartPanel) { + const scoreValue = await score.getVisibleText(); + // Check if the score is a percentage + expect(scoreValue).to.match(/^\d+%$/); + } + }); + + it('Navigation to Findings page', async () => { + const findingsLinkCount = await dashboard.getFindingsLinksCount(TAB_TYPES.CLOUD); + for (let i = 0; i < findingsLinkCount; i++) { + const link = await dashboard.getFindingsLinkAtIndex(TAB_TYPES.CLOUD, i); + // for (const link of findingsLink) { + await link.click(); + await pageObjects.header.waitUntilLoadingHasFinished(); + const groupSelector = await findings.groupSelector(); + await groupSelector.openDropDown(); + await groupSelector.setValue('None'); + expect( + await findings.createDataTableObject('latest_findings_table').getRowsCount() + ).to.be.greaterThan(0); + await cspDashboard.navigateToComplianceDashboardPage(); + await pageObjects.header.waitUntilLoadingHasFinished(); + } + }); + }); + + describe('Kubernetes Dashboard', () => { + it('displays compliance score greater than 80', async () => { + await pageObjects.header.waitUntilLoadingHasFinished(); + const scoreElement = await dashboard.getKubernetesComplianceScore(); + const score = parseInt((await scoreElement.getVisibleText()).replace('%', ''), 10); + expect(score).to.be.greaterThan(80); + }); + + it('displays a number of resources evaluated greater than 150', async () => { + const resourcesEvaluated = await dashboard.getKubernetesResourcesEvaluated(); + const resourcesEvaluatedCount = parseInt( + (await resourcesEvaluated.getVisibleText()).replace(/,/g, ''), + 10 + ); + expect(resourcesEvaluatedCount).greaterThan(150); + }); + + it('Compliance By CIS sections have non empty values', async () => { + const complianceScoresChartPanel = await dashboard.getAllComplianceScoresByCisSection( + 'Kubernetes' + ); + expect(complianceScoresChartPanel.length).to.be.greaterThan(0); + for (const score of complianceScoresChartPanel) { + const scoreValue = await score.getVisibleText(); + // Check if the score is a percentage + expect(scoreValue).to.match(/^\d+%$/); + } + }); + + it('Navigation to Findings page', async () => { + const findingsLinkCount = await dashboard.getFindingsLinksCount(TAB_TYPES.KUBERNETES); + for (let i = 0; i < findingsLinkCount; i++) { + const link = await dashboard.getFindingsLinkAtIndex(TAB_TYPES.KUBERNETES, i); + await link.click(); + await pageObjects.header.waitUntilLoadingHasFinished(); + const groupSelector = await findings.groupSelector(); + await groupSelector.openDropDown(); + await groupSelector.setValue('None'); + expect( + await findings.createDataTableObject('latest_findings_table').getRowsCount() + ).to.be.greaterThan(0); + await cspDashboard.navigateToComplianceDashboardPage(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await dashboard.getKubernetesDashboard(); + } + }); + }); + }); +}; diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/findings_sanity.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/findings_sanity.ts new file mode 100644 index 0000000000000..fd8c8a956b1d5 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/findings_sanity.ts @@ -0,0 +1,175 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'findings', 'header']); + const queryBar = getService('queryBar'); + const testSubjects = getService('testSubjects'); + + describe('Findings Page - Sanity Tests', function () { + this.tags(['cloud_security_posture_ui_sanity']); + let findings: typeof pageObjects.findings; + let latestFindingsTable: typeof findings.latestFindingsTable; + + before(async () => { + findings = pageObjects.findings; + latestFindingsTable = pageObjects.findings.latestFindingsTable; + await findings.navigateToLatestFindingsPage(); + await findings.waitForPluginInitialized(); + }); + + describe('Findings - Querying data', () => { + afterEach(async () => { + // Reset the group selector to None + const groupSelector = await findings.groupSelector(); + await groupSelector.openDropDown(); + await groupSelector.setValue('None'); + // Reset search query + await queryBar.clearQuery(); + await queryBar.submitQuery(); + }); + + const testCases = [ + { + searchQuery: + 'cloud.provider : "aws" and cloud.region : "eu-west-3" and result.evaluation : "failed" and rule.tags : "CIS 5.4"', + provider: 'aws', + expectedRowsCount: 3, + expectedGroupCount: '1 cloud account', + expectedUnitCount: '3 findings', + }, + { + searchQuery: + 'cloud.provider : "gcp" and rule.benchmark.rule_number : "3.1" and result.evaluation : "failed"', + provider: 'gcp', + expectedRowsCount: 1, + expectedGroupCount: '1 cloud account', + expectedUnitCount: '1 finding', + }, + { + searchQuery: + 'cloud.provider : "azure" and rule.benchmark.rule_number : "7.1" and result.evaluation : "failed"', + provider: 'azure', + expectedRowsCount: 1, + expectedGroupCount: '1 cloud account', + expectedUnitCount: '1 finding', + }, + { + searchQuery: + 'rule.benchmark.id : "cis_k8s" and rule.benchmark.rule_number : "4.2.4" and result.evaluation : "failed"', + provider: 'k8s', + expectedRowsCount: 2, + expectedGroupCount: '0 cloud accounts', + expectedUnitCount: '2 findings', + }, + { + searchQuery: 'rule.benchmark.id : "cis_eks" and rule.benchmark.rule_number : "3.1.1"', + provider: 'eks', + expectedRowsCount: 2, + expectedGroupCount: '0 cloud accounts', + expectedUnitCount: '2 findings', + }, + ]; + + testCases.forEach( + ({ searchQuery, provider, expectedRowsCount, expectedGroupCount, expectedUnitCount }) => { + it(`Querying ${provider} provider data`, async () => { + // Execute the query + await queryBar.setQuery(searchQuery); + await queryBar.submitQuery(); + // Get the number of rows in the data table + const rowsCount = await findings + .createDataTableObject('latest_findings_table') + .getRowsCount(); + + // Check that the number of rows matches the expected count + expect(rowsCount).to.be(expectedRowsCount); + const groupSelector = await findings.groupSelector(); + await groupSelector.openDropDown(); + await groupSelector.setValue('Cloud account'); + const grouping = await findings.findingsGrouping(); + // Check that the group count and unit count matches the expected values + const groupCount = await grouping.getGroupCount(); + expect(groupCount).to.be(expectedGroupCount); + + const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be(expectedUnitCount); + }); + } + ); + }); + + describe('Findings - Sorting data', () => { + afterEach(async () => { + const paginationBtn = await testSubjects.find('tablePaginationPopoverButton'); + await paginationBtn.click(); + const pageSizeOption = await testSubjects.find('tablePagination-50-rows'); + await pageSizeOption.click(); + }); + + type SortDirection = 'asc' | 'desc'; + const paginationAndsortingTestCases: Array<{ + searchQuery: string; + paginationRowsCount: string; + columnName: string; + sortType: SortDirection; + expectedResult: string; + }> = [ + { + searchQuery: + 'cloud.provider : "aws" and resource.sub_type : "aws-iam-user" and result.evaluation : "passed"', + paginationRowsCount: '250', + columnName: 'rule.benchmark.rule_number', + sortType: 'desc', + expectedResult: '1.7', + }, + { + searchQuery: 'cloud.provider : "azure" and result.evaluation : "failed"', + paginationRowsCount: '500', + columnName: 'rule.benchmark.rule_number', + sortType: 'asc', + expectedResult: '1.23', + }, + { + searchQuery: 'cloud.provider : "gcp" and result.evaluation : "passed"', + paginationRowsCount: '500', + columnName: 'resource.sub_type', + sortType: 'desc', + expectedResult: 'gcp-storage-bucket', + }, + ]; + + paginationAndsortingTestCases.forEach( + ({ searchQuery, paginationRowsCount, columnName, sortType, expectedResult }) => { + it(`Paginating and sorting data`, async () => { + // Run query + await queryBar.clearQuery(); + await queryBar.setQuery(searchQuery); + await queryBar.submitQuery(); + // Update latest findings table pagination + const paginationBtn = await testSubjects.find('tablePaginationPopoverButton'); + await paginationBtn.click(); + const pageSizeOption = await testSubjects.find( + `tablePagination-${paginationRowsCount}-rows` + ); + await pageSizeOption.click(); + // Sort by column + await latestFindingsTable.toggleColumnSort(columnName, sortType); + await pageObjects.header.waitUntilLoadingHasFinished(); + const values = (await latestFindingsTable.getColumnValues(columnName)).filter(Boolean); + // Check that the first value matches the expected result + // Whole sorting logic functionality is checked in the findings.ts + expect(values[0]).to.equal(expectedResult); + }); + } + ); + }); + }); +}; diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/index.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/index.ts index b08970ccaed13..80afb04563326 100644 --- a/x-pack/test/cloud_security_posture_functional/cloud_tests/index.ts +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/index.ts @@ -10,6 +10,8 @@ import { FtrProviderContext } from '../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function ({ loadTestFile }: FtrProviderContext) { describe('Cloud Security Posture', function () { - loadTestFile(require.resolve('./basic_ui_sanity')); + loadTestFile(require.resolve('./dashboard_sanity')); + loadTestFile(require.resolve('./benchmark_sanity')); + loadTestFile(require.resolve('./findings_sanity')); }); } diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts index 1b2eda553dba2..75ba76b067cc9 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { v4 as uuidv4 } from 'uuid'; import type { FtrProviderContext } from '../ftr_provider_context'; export function AddCisIntegrationFormPageProvider({ @@ -282,6 +283,17 @@ export function AddCisIntegrationFormPageProvider({ return await testSubjects.find(`button-replace-${secretField}`); }; + const inputUniqueIntegrationName = async () => { + const flyout = await testSubjects.find('createPackagePolicy_page'); + const nameField = await flyout.findAllByCssSelector('input[id="name"]'); + await nameField[0].type(uuidv4()); + }; + + const getSecretComponentReplaceButton = async (secretButtonSelector: string) => { + const secretComponentReplaceButton = await testSubjects.find(secretButtonSelector); + return secretComponentReplaceButton; + }; + return { cisAzure, cisAws, @@ -316,5 +328,7 @@ export function AddCisIntegrationFormPageProvider({ isOptionChecked, checkIntegrationPliAuthBlockExists, getReplaceSecretButton, + getSecretComponentReplaceButton, + inputUniqueIntegrationName, }; } diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts index 39856fa34d3fb..25ae1181f38b5 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts @@ -40,6 +40,28 @@ export function BenchmarkPagePageProvider({ getService, getPageObjects }: FtrPro doesBenchmarkTableExists: async () => { return await testSubjects.find('csp_benchmarks_table'); }, + + getBenchmarkTableRows: async () => { + const benchmarkTable = await testSubjects.find(CSP_BECNHMARK_TABLE); + return await benchmarkTable.findAllByXpath(`//tbody//tr`); + }, + + getCellData: async (row: any, cellDataTestSubj: string) => { + const cell = await row.findByTestSubject(cellDataTestSubj); + return await cell.getVisibleText(); + }, + + getEvaluatedCellData: async (row: any) => { + return await benchmarkPage.getCellData(row, 'benchmark-table-column-evaluated'); + }, + + getComplianceCellData: async (row: any) => { + return await benchmarkPage.getCellData(row, 'benchmark-table-column-compliance'); + }, + + getCisNameCellData: async (row: any) => { + return await benchmarkPage.getCellData(row, 'benchmark-table-column-cis-name'); + }, }; const navigateToBenchnmarkPage = async () => { diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts index 4343662e32efd..6f40f7b07003d 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts @@ -52,6 +52,11 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv }, }; + const TAB_TYPES = { + CLOUD: 'Cloud', + KUBERNETES: 'Kubernetes', + } as const; + const dashboard = { getDashboardPageHeader: () => testSubjects.find('cloud-posture-dashboard-page-header'), @@ -63,31 +68,65 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv getCloudTab: async () => { const tabs = await dashboard.getDashboardTabs(); - return await tabs.findByXpath(`//span[text()="Cloud"]`); + return await tabs.findByXpath(`//span[text()="${TAB_TYPES.CLOUD}"]`); }, getKubernetesTab: async () => { const tabs = await dashboard.getDashboardTabs(); - return await tabs.findByXpath(`//span[text()="Kubernetes"]`); + return await tabs.findByXpath(`//span[text()="${TAB_TYPES.KUBERNETES}"]`); }, - clickTab: async (tab: 'Cloud' | 'Kubernetes') => { - if (tab === 'Cloud') { + clickTab: async (tab: typeof TAB_TYPES[keyof typeof TAB_TYPES]) => { + if (tab === TAB_TYPES.CLOUD) { const cloudTab = await dashboard.getCloudTab(); await cloudTab.click(); } - if (tab === 'Kubernetes') { + if (tab === TAB_TYPES.KUBERNETES) { const k8sTab = await dashboard.getKubernetesTab(); await k8sTab.click(); } }, + getAllComplianceScoresByCisSection: async (tab: typeof TAB_TYPES[keyof typeof TAB_TYPES]) => { + await dashboard.getDashoard(tab); + const pageContainer = await testSubjects.find('pageContainer'); + return await pageContainer.findAllByTestSubject('cloudSecurityFindingsComplianceScore'); + }, + + getDashoard: async (tab: typeof TAB_TYPES[keyof typeof TAB_TYPES]) => { + if (tab === TAB_TYPES.CLOUD) { + return await dashboard.getCloudDashboard(); + } + if (tab === TAB_TYPES.KUBERNETES) { + return await dashboard.getKubernetesDashboard(); + } + }, + + getFindingsLinks: async (tab: typeof TAB_TYPES[keyof typeof TAB_TYPES]) => { + await dashboard.getDashoard(tab); + const pageContainer = await testSubjects.find('pageContainer'); + return await pageContainer.findAllByXpath(`//button[contains(@class, 'euiLink')]`); + }, + + getFindingsLinkAtIndex: async ( + tab: typeof TAB_TYPES[keyof typeof TAB_TYPES], + linkIndex = 0 + ) => { + const allLinks = await dashboard.getFindingsLinks(tab); + return await allLinks[linkIndex]; + }, + + getFindingsLinksCount: async (tab: typeof TAB_TYPES[keyof typeof TAB_TYPES]) => { + const allLinks = await dashboard.getFindingsLinks(tab); + return await allLinks.length; + }, + getIntegrationDashboardContainer: () => testSubjects.find('dashboard-container'), // Cloud Dashboard getCloudDashboard: async () => { - await dashboard.clickTab('Cloud'); + await dashboard.clickTab(TAB_TYPES.CLOUD); return await testSubjects.find('cloud-dashboard-container'); }, @@ -119,7 +158,7 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv // Kubernetes Dashboard getKubernetesDashboard: async () => { - await dashboard.clickTab('Kubernetes'); + await dashboard.clickTab(TAB_TYPES.KUBERNETES); return await testSubjects.find('kubernetes-dashboard-container'); }, @@ -161,5 +200,6 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv navigateToComplianceDashboardPage, dashboard, index, + TAB_TYPES, }; } diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts index 1b57e9a65d41e..a91d35ea668be 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts @@ -23,12 +23,13 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.navigateToAddIntegrationCspmPage(); }); - // FLAKY: https://github.com/elastic/kibana/issues/186302 - describe.skip('CNVM AWS', () => { + describe('CNVM AWS', () => { it('Hyperlink on PostInstallation Modal should have the correct URL', async () => { await cisIntegration.navigateToAddIntegrationCnvmPage(); + await cisIntegration.inputUniqueIntegrationName(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( (await cisIntegration.getUrlOnPostInstallModal()) === 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html' @@ -50,8 +51,10 @@ export default function (providerContext: FtrProviderContext) { it('Clicking on Launch CloudFormation on post intall modal should lead user to Cloud Formation page', async () => { await cisIntegration.navigateToAddIntegrationCnvmPage(); + await cisIntegration.inputUniqueIntegrationName(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( ( await cisIntegration.clickLaunchAndGetCurrentUrl( diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts index 5523e05120912..10c2f0560237c 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts @@ -48,11 +48,11 @@ export default function (providerContext: FtrProviderContext) { it('Hyperlink on PostInstallation Modal should have the correct URL', async () => { await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegrationAws.getPostInstallCloudFormationModal()) !== undefined).to.be( true ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( (await cisIntegration.getUrlOnPostInstallModal()) === 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html' @@ -73,7 +73,7 @@ export default function (providerContext: FtrProviderContext) { it('Clicking on Launch CloudFormation on post intall modal should lead user to Cloud Formation page', async () => { await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( ( await cisIntegration.clickLaunchAndGetCurrentUrl( @@ -92,7 +92,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(ROLE_ARN_TEST_ID, roleArn); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect((await cisIntegration.getFieldValueInEditPage(ROLE_ARN_TEST_ID)) === roleArn).to.be( @@ -109,7 +109,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.clickOptionButton(AWS_CREDENTIAL_SELECTOR); await cisIntegration.selectValue(AWS_CREDENTIAL_SELECTOR, 'direct_access_keys'); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(DIRECT_ACCESS_KEY_ID_TEST_ID, directAccessKeyId); await cisIntegration.fillInTextField( @@ -117,7 +117,7 @@ export default function (providerContext: FtrProviderContext) { directAccessSecretKey ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect( @@ -137,7 +137,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.clickOptionButton(AWS_CREDENTIAL_SELECTOR); await cisIntegration.selectValue(AWS_CREDENTIAL_SELECTOR, 'temporary_keys'); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(TEMP_ACCESS_KEY_ID_TEST_ID, accessKeyId); await cisIntegration.fillInTextField( @@ -149,7 +149,7 @@ export default function (providerContext: FtrProviderContext) { tempAccessSessionToken ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); @@ -172,7 +172,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.clickOptionButton(AWS_CREDENTIAL_SELECTOR); await cisIntegration.selectValue(AWS_CREDENTIAL_SELECTOR, 'shared_credentials'); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(SHARED_CREDENTIALS_FILE_TEST_ID, sharedCredentialFile); await cisIntegration.fillInTextField( @@ -180,7 +180,7 @@ export default function (providerContext: FtrProviderContext) { sharedCredentialProfileName ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); @@ -200,7 +200,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID); await cisIntegration.clickOptionButton(AWS_SINGLE_ACCOUNT_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( (await cisIntegration.getUrlOnPostInstallModal()) === 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html' @@ -216,7 +216,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(ROLE_ARN_TEST_ID, roleArn); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect((await cisIntegration.getFieldValueInEditPage(ROLE_ARN_TEST_ID)) === roleArn).to.be( @@ -234,7 +234,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.clickOptionButton(AWS_CREDENTIAL_SELECTOR); await cisIntegration.selectValue(AWS_CREDENTIAL_SELECTOR, 'direct_access_keys'); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(DIRECT_ACCESS_KEY_ID_TEST_ID, directAccessKeyId); await cisIntegration.fillInTextField( @@ -242,7 +242,7 @@ export default function (providerContext: FtrProviderContext) { directAccessSecretKey ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect( @@ -263,7 +263,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.clickOptionButton(AWS_CREDENTIAL_SELECTOR); await cisIntegration.selectValue(AWS_CREDENTIAL_SELECTOR, 'temporary_keys'); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(TEMP_ACCESS_KEY_ID_TEST_ID, accessKeyId); await cisIntegration.fillInTextField( @@ -275,7 +275,7 @@ export default function (providerContext: FtrProviderContext) { tempAccessSessionToken ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); @@ -299,7 +299,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.clickOptionButton(AWS_CREDENTIAL_SELECTOR); await cisIntegration.selectValue(AWS_CREDENTIAL_SELECTOR, 'shared_credentials'); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(AWS_MANUAL_TEST_ID); await cisIntegration.fillInTextField(SHARED_CREDENTIALS_FILE_TEST_ID, sharedCredentialFile); await cisIntegration.fillInTextField( @@ -307,7 +307,7 @@ export default function (providerContext: FtrProviderContext) { sharedCredentialProfileName ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts index 99cc57905d46e..4415ec5a34160 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts @@ -53,7 +53,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(CIS_AZURE_OPTION_TEST_ID); await cisIntegration.clickOptionButton(CIS_AZURE_SETUP_FORMAT_TEST_SUBJECTS.ARM_TEMPLATE); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegrationAzure.getPostInstallArmTemplateModal()) !== undefined).to.be( true ); @@ -70,7 +70,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(CIS_AZURE_SETUP_FORMAT_TEST_SUBJECTS.MANUAL); await cisIntegration.selectValue(AZURE_CREDENTIAL_SELECTOR, 'managed_identity'); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); }); }); @@ -83,7 +83,7 @@ export default function (providerContext: FtrProviderContext) { AZURE_CREDENTIAL_SELECTOR, 'service_principal_with_client_secret' ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.fillInTextField( CIS_AZURE_INPUT_FIELDS_TEST_SUBJECTS.CLIENT_ID, clientId @@ -97,7 +97,7 @@ export default function (providerContext: FtrProviderContext) { clientSecret ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); @@ -123,7 +123,7 @@ export default function (providerContext: FtrProviderContext) { AZURE_CREDENTIAL_SELECTOR, 'service_principal_with_client_certificate' ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.fillInTextField( CIS_AZURE_INPUT_FIELDS_TEST_SUBJECTS.CLIENT_ID, clientId @@ -137,7 +137,7 @@ export default function (providerContext: FtrProviderContext) { clientCertificatePath ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); @@ -164,7 +164,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(CIS_AZURE_OPTION_TEST_ID); await cisIntegration.clickOptionButton(CIS_AZURE_SINGLE_SUB_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegrationAzure.getPostInstallArmTemplateModal()) !== undefined).to.be( true ); @@ -182,7 +182,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(CIS_AZURE_SETUP_FORMAT_TEST_SUBJECTS.MANUAL); await cisIntegration.selectValue(AZURE_CREDENTIAL_SELECTOR, 'managed_identity'); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); }); }); @@ -195,7 +195,7 @@ export default function (providerContext: FtrProviderContext) { AZURE_CREDENTIAL_SELECTOR, 'service_principal_with_client_secret' ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.fillInTextField( CIS_AZURE_INPUT_FIELDS_TEST_SUBJECTS.CLIENT_ID, clientId @@ -209,7 +209,7 @@ export default function (providerContext: FtrProviderContext) { clientSecret ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); @@ -235,7 +235,7 @@ export default function (providerContext: FtrProviderContext) { AZURE_CREDENTIAL_SELECTOR, 'service_principal_with_client_certificate' ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.fillInTextField( CIS_AZURE_INPUT_FIELDS_TEST_SUBJECTS.CLIENT_ID, clientId @@ -249,7 +249,7 @@ export default function (providerContext: FtrProviderContext) { clientCertificatePath ); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts index 121bf7cc2e574..e8d1dcda1f430 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts @@ -17,7 +17,7 @@ const PRJ_ID_TEST_ID = 'project_id_test_id'; const ORG_ID_TEST_ID = 'organization_id_test_id'; const CREDENTIALS_TYPE_TEST_ID = 'credentials_type_test_id'; const CREDENTIALS_FILE_TEST_ID = 'credentials_file_test_id'; -const CREDENTIALS_JSON_TEST_ID = 'credentials_json_test_id'; +const CREDENTIALS_JSON_TEST_ID = 'textAreaInput-credentials-json'; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { @@ -63,7 +63,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(GCP_ORGANIZATION_TEST_ID); await cisIntegration.clickOptionButton(GCP_CLOUD_SHELL_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegrationGcp.isPostInstallGoogleCloudShellModal(true)) === true).to.be( true ); @@ -79,7 +79,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.fillInTextField('organization_id_test_id', organizationName); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( (await cisIntegrationGcp.isPostInstallGoogleCloudShellModal( true, @@ -102,7 +102,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(GCP_CLOUD_SHELL_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegrationGcp.isPostInstallGoogleCloudShellModal(false)) === true).to.be( true ); @@ -111,7 +111,7 @@ export default function (providerContext: FtrProviderContext) { it('Hyperlink on PostInstallation Modal should have the correct URL', async () => { await cisIntegration.clickOptionButton(CIS_GCP_OPTION_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( (await cisIntegration.getUrlOnPostInstallModal()) === 'https://cloud.google.com/shell/docs' @@ -121,7 +121,7 @@ export default function (providerContext: FtrProviderContext) { it('Clicking on Launch CloudShell on post intall modal should lead user to CloudShell page', async () => { await cisIntegration.clickOptionButton(CIS_GCP_OPTION_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( ( await cisIntegration.clickLaunchAndGetCurrentUrl( @@ -143,7 +143,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.fillInTextField(PRJ_ID_TEST_ID, projectName); await cisIntegration.fillInTextField(CREDENTIALS_FILE_TEST_ID, credentialFileName); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect( @@ -167,12 +167,14 @@ export default function (providerContext: FtrProviderContext) { ); await cisIntegration.fillInTextField(CREDENTIALS_JSON_TEST_ID, credentialJsonName); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); + await cisIntegration.clickFirstElementOnIntegrationTable(); expect( - (await cisIntegration.getFieldValueInEditPage(CREDENTIALS_JSON_TEST_ID)) === - credentialJsonName + (await cisIntegration.getSecretComponentReplaceButton( + 'button-replace-credentials-json' + )) !== undefined ).to.be(true); }); }); @@ -183,7 +185,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(GCP_SINGLE_ACCOUNT_TEST_ID); await cisIntegration.clickOptionButton(GCP_CLOUD_SHELL_TEST_ID); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegrationGcp.isPostInstallGoogleCloudShellModal(false)) === true).to.be( true ); @@ -195,7 +197,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.clickOptionButton(GCP_CLOUD_SHELL_TEST_ID); await cisIntegration.fillInTextField('project_id_test_id', projectName); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect( (await cisIntegrationGcp.isPostInstallGoogleCloudShellModal(false, '', projectName)) === true @@ -230,7 +232,7 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.fillInTextField(PRJ_ID_TEST_ID, projectName); await cisIntegration.fillInTextField(CREDENTIALS_FILE_TEST_ID, credentialFileName); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect( @@ -248,7 +250,7 @@ export default function (providerContext: FtrProviderContext) { ); await cisIntegration.fillInTextField(CREDENTIALS_JSON_TEST_ID, credentialJsonName); await cisIntegration.clickSaveIntegrationButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.navigateToIntegrationCspList(); expect( (await cisIntegration.getFieldValueInEditPage(CREDENTIALS_JSON_TEST_ID)) === @@ -268,12 +270,14 @@ export default function (providerContext: FtrProviderContext) { ); await cisIntegration.fillInTextField(CREDENTIALS_JSON_TEST_ID, credentialJsonName); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); + await cisIntegration.clickFirstElementOnIntegrationTable(); expect( - (await cisIntegration.getFieldValueInEditPage(CREDENTIALS_JSON_TEST_ID)) === - credentialJsonName + (await cisIntegration.getSecretComponentReplaceButton( + 'button-replace-credentials-json' + )) !== undefined ).to.be(true); }); it('Users are able to switch credentials_type from/to Credential File fields ', async () => { @@ -286,7 +290,7 @@ export default function (providerContext: FtrProviderContext) { ); await cisIntegration.fillInTextField(CREDENTIALS_FILE_TEST_ID, credentialFileName); await cisIntegration.clickSaveIntegrationButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.navigateToIntegrationCspList(); expect( (await cisIntegration.getFieldValueInEditPage(CREDENTIALS_FILE_TEST_ID)) === diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_eks.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_eks.ts index db567d335b0ff..f8d7736938f92 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_eks.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_eks.ts @@ -35,14 +35,14 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.navigateToAddIntegrationKspmPage(); }); - // FLAKY: https://github.com/elastic/kibana/issues/186306 - describe.skip('KSPM EKS Assume Role', async () => { + describe('KSPM EKS Assume Role', async () => { it('KSPM EKS Assume Role workflow', async () => { const roleArn = 'RoleArnTestValue'; await cisIntegration.clickOptionButton(CIS_EKS_OPTION_TEST_ID); + await cisIntegration.inputUniqueIntegrationName(); await cisIntegration.fillInTextField(ROLE_ARN_TEST_ID, roleArn); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect((await cisIntegration.getFieldValueInEditPage(ROLE_ARN_TEST_ID)) === roleArn).to.be( @@ -51,22 +51,23 @@ export default function (providerContext: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/186315 - describe.skip('KSPM EKS Direct Access', async () => { + describe('KSPM EKS Direct Access', async () => { it('KSPM EKS Direct Access Workflow', async () => { const directAccessKeyId = 'directAccessKeyIdTest'; const directAccessSecretKey = 'directAccessSecretKeyTest'; await cisIntegration.clickOptionButton(CIS_EKS_OPTION_TEST_ID); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(EKS_DIRECT_ACCESS_TEST_ID); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await cisIntegration.inputUniqueIntegrationName(); await cisIntegration.fillInTextField(DIRECT_ACCESS_KEY_ID_TEST_ID, directAccessKeyId); await cisIntegration.fillInTextField( DIRECT_ACCESS_SECRET_KEY_TEST_ID, directAccessSecretKey ); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); expect( @@ -77,16 +78,16 @@ export default function (providerContext: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/186389 - describe.skip('KSPM EKS Temporary Keys', () => { + describe('KSPM EKS Temporary Keys', () => { it('KSPM EKS Temporary Keys Workflow', async () => { const accessKeyId = 'accessKeyIdTest'; const accessKeySecretKey = 'accessKeySecretKeyTest'; const tempAccessSessionToken = 'tempAccessSessionTokenTest'; await cisIntegration.clickOptionButton(CIS_EKS_OPTION_TEST_ID); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(EKS_TEMPORARY_KEYS_TEST_ID); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await cisIntegration.inputUniqueIntegrationName(); await cisIntegration.fillInTextField(TEMP_ACCESS_KEY_ID_TEST_ID, accessKeyId); await cisIntegration.fillInTextField( TEMP_ACCESS_KEY_SECRET_KEY_TEST_ID, @@ -96,8 +97,9 @@ export default function (providerContext: FtrProviderContext) { TEMP_ACCESS_SESSION_TOKEN_TEST_ID, tempAccessSessionToken ); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); @@ -112,22 +114,23 @@ export default function (providerContext: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/186387 - describe.skip('KSPM EKS Shared Credentials', () => { + describe('KSPM EKS Shared Credentials', () => { it('KSPM EKS Shared Credentials Workflow', async () => { const sharedCredentialFile = 'sharedCredentialFileTest'; const sharedCredentialProfileName = 'sharedCredentialProfileNameTest'; await cisIntegration.clickOptionButton(CIS_EKS_OPTION_TEST_ID); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickOptionButton(EKS_SHARED_CREDENTIAL_TEST_ID); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await cisIntegration.inputUniqueIntegrationName(); await cisIntegration.fillInTextField(SHARED_CREDENTIALS_FILE_TEST_ID, sharedCredentialFile); await cisIntegration.fillInTextField( SHARED_CREDETIALS_PROFILE_NAME_TEST_ID, sharedCredentialProfileName ); + await pageObjects.header.waitUntilLoadingHasFinished(); await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_k8s.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_k8s.ts index ae2ede4046d11..872c786e3ce28 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_k8s.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/kspm/cis_integration_k8s.ts @@ -25,7 +25,7 @@ export default function (providerContext: FtrProviderContext) { describe('KSPM K8S', () => { it('KSPM K8S Workflow', async () => { await cisIntegration.clickSaveButton(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); await cisIntegration.navigateToIntegrationCspList(); await cisIntegration.clickFirstElementOnIntegrationTable(); diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings.ts b/x-pack/test/cloud_security_posture_functional/pages/findings.ts index 16e63fac3491e..3ea3475b85763 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/findings.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/findings.ts @@ -139,7 +139,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'Findings table to be loaded', async () => (await latestFindingsTable.getRowsCount()) === data.length ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); }); afterEach(async () => { @@ -172,7 +172,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { for (const [columnName, dir, sortingMethod] of testCases) { await latestFindingsTable.toggleColumnSort(columnName, dir); /* This sleep or delay is added to allow some time for the column to settle down before we get the value and to prevent the test from getting the wrong value*/ - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); const values = (await latestFindingsTable.getColumnValues(columnName)).filter(Boolean); expect(values).to.not.be.empty(); const sorted = values @@ -297,7 +297,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'Findings table to be loaded', async () => (await latestFindingsTable.getRowsCount()) === data.length + 1 ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await distributionBar.filterBy('passed'); @@ -328,7 +328,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'Findings table to be loaded', async () => (await latestFindingsTable.getRowsCount()) === data.length ); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); await distributionBar.filterBy('passed'); @@ -346,7 +346,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await cspSecurity.logout(); await cspSecurity.login('csp_read_user'); await findings.navigateToLatestFindingsPage(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect(await latestFindingsTable.getRowsCount()).to.be.greaterThan(0); }); @@ -356,7 +356,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await findings.navigateToLatestFindingsPage(); - pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.header.waitUntilLoadingHasFinished(); expect(await findings.getUnprivilegedPrompt()); }); }); diff --git a/x-pack/test/fleet_api_integration/apis/policy_secrets.ts b/x-pack/test/fleet_api_integration/apis/policy_secrets.ts index 226c22d6ca924..d8e641b7af0a5 100644 --- a/x-pack/test/fleet_api_integration/apis/policy_secrets.ts +++ b/x-pack/test/fleet_api_integration/apis/policy_secrets.ts @@ -847,6 +847,8 @@ export default function (providerContext: FtrProviderContext) { it('should not store secrets if fleet server does not meet minimum version', async () => { const { fleetServerAgentPolicy } = await createFleetServerAgentPolicy(); await createFleetServerAgent(fleetServerAgentPolicy.id, 'server_1', '7.0.0'); + const { fleetServerAgentPolicy: fleetServerPolicy2 } = await createFleetServerAgentPolicy(); // extra policy to verify `or` condition + await createFleetServerAgent(fleetServerPolicy2.id, 'server_1', '8.12.0'); await callFleetSetup(); @@ -865,7 +867,10 @@ export default function (providerContext: FtrProviderContext) { }); it('should not store secrets if there are no fleet servers', async () => { + await createFleetServerAgentPolicy(); const agentPolicy = await createAgentPolicy(); + // agent with new version shouldn't make storage secrets enabled + await createFleetServerAgent(agentPolicy.id, 'server_2', '8.12.0'); const packagePolicyWithSecrets = await createPackagePolicyWithSecrets(agentPolicy.id); // secret should be in plain text i.e not a secret refrerence diff --git a/x-pack/test/fleet_cypress/artifact_manager.ts b/x-pack/test/fleet_cypress/artifact_manager.ts index 0fe6609f28efc..23efabe2d976a 100644 --- a/x-pack/test/fleet_cypress/artifact_manager.ts +++ b/x-pack/test/fleet_cypress/artifact_manager.ts @@ -15,6 +15,10 @@ export async function getLatestVersion(): Promise { return pRetry(() => axios('https://artifacts-api.elastic.co/v1/versions'), { maxRetryTime: 60 * 1000, // 1 minute }) - .then((response) => last(response.data.versions as string[]) || DEFAULT_VERSION) + .then( + (response) => + last((response.data.versions as string[]).filter((v) => v.includes('-SNAPSHOT'))) || + DEFAULT_VERSION + ) .catch(() => DEFAULT_VERSION); } diff --git a/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts b/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts index 6b96291734bcf..52ccfad201a53 100644 --- a/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts +++ b/x-pack/test/functional/apps/dataset_quality/dataset_quality_flyout.ts @@ -50,8 +50,7 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid const degradedDatasetName = datasetNames[2]; - // Failing: See https://github.com/elastic/kibana/issues/187589 - describe.skip('Flyout', () => { + describe('Flyout', () => { before(async () => { // Install Apache Integration and ingest logs for it await PageObjects.observabilityLogsExplorer.installPackage(apachePkg); @@ -150,17 +149,19 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid it('should shows the integration section for integrations', async () => { await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); - const integrationNameElements = await PageObjects.datasetQuality.getFlyoutElementsByText( - '[data-test-subj=datasetQualityFlyoutFieldValue]', - apacheIntegrationId - ); - await testSubjects.existOrFail( PageObjects.datasetQuality.testSubjectSelectors .datasetQualityFlyoutFieldsListIntegrationDetails ); - expect(integrationNameElements.length).to.eql(1); + await retry.tryForTime(5000, async () => { + const integrationNameExists = await PageObjects.datasetQuality.doesTextExist( + PageObjects.datasetQuality.testSubjectSelectors + .datasetQualityFlyoutFieldsListIntegrationDetails, + apacheIntegrationId + ); + expect(integrationNameExists).to.be(true); + }); await PageObjects.datasetQuality.closeFlyout(); }); diff --git a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts index 571594a8b94b6..f8ba7904280b9 100644 --- a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts @@ -31,20 +31,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const kibanaServer = getService('kibanaServer'); describe('Infrastructure Source Configuration', function () { - before(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); + before(async () => kibanaServer.savedObjects.cleanStandardList()); + after(async () => kibanaServer.savedObjects.cleanStandardList()); describe('with metrics present', () => { - before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); - }); - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); - }); + before(async () => + esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs') + ); + after(async () => + esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs') + ); it('renders the waffle map', async () => { await pageObjects.common.navigateToApp('infraOps'); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts index bdd3052f54c0a..3a60e8fca97c2 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts @@ -39,8 +39,7 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); - // Failing: See https://github.com/elastic/kibana/issues/164381 - describe.skip('forecasts', function () { + describe('forecasts', function () { this.tags(['ml']); describe('with single metric job', function () { diff --git a/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts index 1974a48e77841..d6729e7bef923 100644 --- a/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts @@ -10,8 +10,9 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../services/ml/security_common'; export default function ({ getPageObjects, getService }: FtrProviderContext) { - const PageObjects = getPageObjects(['common', 'error']); + const PageObjects = getPageObjects(['common', 'error', 'dashboard']); const ml = getService('ml'); + const esArchiver = getService('esArchiver'); const testUsers = [{ user: USER.ML_UNAUTHORIZED, discoverAvailable: true }]; @@ -56,5 +57,28 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); } + + describe('for user with no ML access and Kibana features access', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.securityUI.loginAs(USER.ML_DISABLED); + await ml.api.cleanMlIndices(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await ml.securityUI.logout(); + }); + + it('should not register ML embeddables in the dashboard', async () => { + await ml.testExecution.logTestStep( + 'should not contain ML embeddable in the Add panel list' + ); + await PageObjects.dashboard.navigateToApp(); + await PageObjects.dashboard.clickCreateDashboardPrompt(); + await ml.dashboardEmbeddables.assertMlSectionExists(false); + }); + }); }); } diff --git a/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts b/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts index 0afa2979a6c15..0fb7675882016 100644 --- a/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts +++ b/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts @@ -15,7 +15,6 @@ import { LlmProxy, } from '../../../observability_ai_assistant_api_integration/common/create_llm_proxy'; -const indexName = 'basic_index'; const esArchiveIndex = 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'; export default function (ftrContext: FtrProviderContext) { @@ -56,6 +55,7 @@ export default function (ftrContext: FtrProviderContext) { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToExist(); await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToDisabled(); await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageIndexButtonExists(); }); describe('with gen ai connectors', () => { @@ -68,8 +68,8 @@ export default function (ftrContext: FtrProviderContext) { await removeOpenAIConnector?.(); await browser.refresh(); }); - it('hide gen ai panel', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnector(); + it('show success llm button', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectShowSuccessLLMButton(); }); }); @@ -80,7 +80,7 @@ export default function (ftrContext: FtrProviderContext) { it('creates a connector successfully', async () => { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenConnectorPagePlayground(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnectorAfterCreatingConnector( + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSuccessButtonAfterCreatingConnector( createConnector ); }); @@ -92,18 +92,16 @@ export default function (ftrContext: FtrProviderContext) { }); describe('without any indices', () => { - it('show no index callout', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectNoIndexCalloutExists(); + it('show create index button', async () => { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToExists(); }); - it('hide no index callout when index added', async () => { + it('show success button when index added', async () => { await createIndex(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenFlyoutAndSelectIndex(); }); after(async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.removeIndexFromComboBox(); await esArchiver.unload(esArchiveIndex); await browser.refresh(); }); @@ -116,14 +114,8 @@ export default function (ftrContext: FtrProviderContext) { await browser.refresh(); }); - it('dropdown shows up', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectIndicesInDropdown(); - }); - - it('can select index from dropdown and navigate to chat window', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToSelectIndicesAndStartButtonEnabled( - indexName - ); + it('can select index from dropdown and load chat page', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToSelectIndicesAndLoadChat(); }); after(async () => { @@ -139,7 +131,7 @@ export default function (ftrContext: FtrProviderContext) { await createConnector(); await createIndex(); await browser.refresh(); - await pageObjects.searchPlayground.PlaygroundChatPage.navigateToChatPage(indexName); + await pageObjects.searchPlayground.PlaygroundChatPage.navigateToChatPage(); }); it('loads successfully', async () => { await pageObjects.searchPlayground.PlaygroundChatPage.expectChatWindowLoaded(); diff --git a/x-pack/test/functional/page_objects/dataset_quality.ts b/x-pack/test/functional/page_objects/dataset_quality.ts index 7cb633a8113b6..7f918ce3ba76f 100644 --- a/x-pack/test/functional/page_objects/dataset_quality.ts +++ b/x-pack/test/functional/page_objects/dataset_quality.ts @@ -347,12 +347,14 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv return refreshButton.click(); }, - async getFlyoutElementsByText(selector: string, text: string) { - const flyoutContainer: WebElementWrapper = await testSubjects.find( - testSubjectSelectors.datasetQualityFlyout - ); + async doesTextExist(selector: string, text: string) { + const textValues = await testSubjects.getVisibleTextAll(selector); + if (textValues && textValues.length > 0) { + const values = textValues[0].split('\n'); + return values.includes(text); + } - return getAllByText(flyoutContainer, selector, text); + return false; }, getFlyoutLogsExplorerButton() { @@ -373,15 +375,6 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv ); }, - async doestTextExistInFlyout(text: string, elementSelector: string) { - const flyoutContainer: WebElementWrapper = await testSubjects.find( - testSubjectSelectors.datasetQualityFlyoutBody - ); - - const elements = await getAllByText(flyoutContainer, elementSelector, text); - return elements.length > 0; - }, - // `excludeKeys` needed to circumvent `_stats` not available in Serverless https://github.com/elastic/kibana/issues/178954 // TODO: Remove `excludeKeys` when `_stats` is available in Serverless async parseFlyoutKpis(excludeKeys: string[] = []): Promise { @@ -554,28 +547,3 @@ async function getDatasetTableHeaderTexts(tableWrapper: WebElementWrapper) { headerElementWrappers.map((headerElementWrapper) => headerElementWrapper.getVisibleText()) ); } - -/** - * Get all elements matching the given selector and text - * @example - * const container = await testSubjects.find('myContainer'); - * const elements = await getAllByText(container, 'button', 'Click me'); - * - * @param container { WebElementWrapper } The container to search within - * @param selector { string } The selector to search for (or filter elements by) - * @param text { string } The text to search for within the filtered elements - */ -export async function getAllByText(container: WebElementWrapper, selector: string, text: string) { - const elements = await container.findAllByCssSelector(selector); - const matchingElements: WebElementWrapper[] = []; - - for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - const elementText = await element.getVisibleText(); - if (elementText === text) { - matchingElements.push(element); - } - } - - return matchingElements; -} diff --git a/x-pack/test/functional/page_objects/search_playground_page.ts b/x-pack/test/functional/page_objects/search_playground_page.ts index 89bffc8bbd841..01f50245a1a90 100644 --- a/x-pack/test/functional/page_objects/search_playground_page.ts +++ b/x-pack/test/functional/page_objects/search_playground_page.ts @@ -10,16 +10,28 @@ import { FtrProviderContext } from '../ftr_provider_context'; export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); - const comboBox = getService('comboBox'); const browser = getService('browser'); + const selectIndex = async () => { + await testSubjects.existOrFail('addDataSourcesButton'); + await testSubjects.click('addDataSourcesButton'); + await testSubjects.existOrFail('selectIndicesFlyout'); + await testSubjects.click('sourceIndex-0'); + await testSubjects.click('saveButton'); + }; return { PlaygroundStartChatPage: { async expectPlaygroundStartChatPageComponentsToExist() { - await testSubjects.existOrFail('startChatPage'); - await testSubjects.existOrFail('connectToLLMChatPanel'); - await testSubjects.existOrFail('selectIndicesChatPanel'); - await testSubjects.existOrFail('startChatButton'); + await testSubjects.existOrFail('setupPage'); + await testSubjects.existOrFail('connectLLMButton'); + }, + + async expectPlaygroundStartChatPageIndexButtonExists() { + await testSubjects.existOrFail('createIndexButton'); + }, + + async expectPlaygroundStartChatPageIndexCalloutExists() { + await testSubjects.existOrFail('createIndexCallout'); }, async expectPlaygroundHeaderComponentsToExist() { @@ -28,8 +40,8 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) }, async expectPlaygroundHeaderComponentsToDisabled() { - expect(await testSubjects.isEnabled('editContextActionButton')).to.be(false); - expect(await testSubjects.isEnabled('viewQueryActionButton')).to.be(false); + expect(await testSubjects.getAttribute('viewModeSelector', 'disabled')).to.be('true'); + expect(await testSubjects.isEnabled('dataSourceActionButton')).to.be(false); expect(await testSubjects.isEnabled('viewCodeActionButton')).to.be(false); }, @@ -37,66 +49,45 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) await testSubjects.existOrFail('createIndexButton'); }, - async expectNoIndexCalloutExists() { - await testSubjects.existOrFail('createIndexCallout'); - }, - - async expectSelectIndex(indexName: string) { + async expectOpenFlyoutAndSelectIndex() { await browser.refresh(); - await testSubjects.missingOrFail('createIndexCallout'); - await testSubjects.existOrFail('selectIndicesComboBox'); - await comboBox.setCustom('selectIndicesComboBox', indexName); + await selectIndex(); + await testSubjects.existOrFail('dataSourcesSuccessButton'); }, - async expectIndicesInDropdown() { - await testSubjects.existOrFail('selectIndicesComboBox'); - }, - - async removeIndexFromComboBox() { - await testSubjects.click('removeIndexButton'); - }, - - async expectToSelectIndicesAndStartButtonEnabled(indexName: string) { - await comboBox.setCustom('selectIndicesComboBox', indexName); - expect(await testSubjects.isEnabled('startChatButton')).to.be(true); - expect(await testSubjects.isEnabled('editContextActionButton')).to.be(true); - expect(await testSubjects.isEnabled('viewQueryActionButton')).to.be(true); - expect(await testSubjects.isEnabled('viewCodeActionButton')).to.be(true); - - await testSubjects.click('startChatButton'); + async expectToSelectIndicesAndLoadChat() { + await selectIndex(); await testSubjects.existOrFail('chatPage'); }, async expectAddConnectorButtonExists() { - await testSubjects.existOrFail('setupGenAIConnectorButton'); + await testSubjects.existOrFail('connectLLMButton'); }, async expectOpenConnectorPagePlayground() { - await testSubjects.click('setupGenAIConnectorButton'); + await testSubjects.click('connectLLMButton'); await testSubjects.existOrFail('create-connector-flyout'); }, - async expectHideGenAIPanelConnectorAfterCreatingConnector( - createConnector: () => Promise - ) { + async expectSuccessButtonAfterCreatingConnector(createConnector: () => Promise) { await createConnector(); await browser.refresh(); - await testSubjects.missingOrFail('connectToLLMChatPanel'); + await testSubjects.existOrFail('successConnectLLMButton'); }, - async expectHideGenAIPanelConnector() { - await testSubjects.missingOrFail('connectToLLMChatPanel'); + async expectShowSuccessLLMButton() { + await testSubjects.existOrFail('successConnectLLMButton'); }, }, PlaygroundChatPage: { - async navigateToChatPage(indexName: string) { - await comboBox.setCustom('selectIndicesComboBox', indexName); - await testSubjects.click('startChatButton'); + async navigateToChatPage() { + await selectIndex(); + await testSubjects.existOrFail('chatPage'); }, async expectChatWindowLoaded() { - expect(await testSubjects.isEnabled('editContextActionButton')).to.be(true); - expect(await testSubjects.isEnabled('viewQueryActionButton')).to.be(true); + expect(await testSubjects.getAttribute('viewModeSelector', 'disabled')).to.be(null); + expect(await testSubjects.isEnabled('dataSourceActionButton')).to.be(true); expect(await testSubjects.isEnabled('viewCodeActionButton')).to.be(true); expect(await testSubjects.isEnabled('regenerateActionButton')).to.be(false); @@ -114,9 +105,8 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) await (await testSubjects.find('manageConnectorsLink')).getAttribute('href') ).to.contain('/app/management/insightsAndAlerting/triggersActionsConnectors/connectors/'); - await testSubjects.click('sourcesAccordion'); - - expect(await testSubjects.findAll('indicesInAccordian')).to.have.length(1); + await testSubjects.existOrFail('editContextPanel'); + await testSubjects.existOrFail('summarizationPanel'); }, async sendQuestion() { @@ -145,9 +135,9 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) }, async expectViewQueryHasFields() { - await testSubjects.click('viewQueryActionButton'); - await testSubjects.existOrFail('viewQueryFlyout'); - const fields = await testSubjects.findAll('queryField'); + await testSubjects.existOrFail('queryMode'); + await testSubjects.click('queryMode'); + const fields = await testSubjects.findAll('fieldName'); expect(fields.length).to.be(1); @@ -156,18 +146,16 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) expect(code.replace(/ /g, '')).to.be( '{\n"retriever":{\n"standard":{\n"query":{\n"multi_match":{\n"query":"{query}",\n"fields":[\n"baz"\n]\n}\n}\n}\n}\n}' ); - await testSubjects.click('euiFlyoutCloseButton'); }, async expectEditContextOpens() { - await testSubjects.click('editContextActionButton'); - await testSubjects.existOrFail('editContextFlyout'); - await testSubjects.click('contextFieldsSelectable_basic_index'); + await testSubjects.click('chatMode'); + await testSubjects.existOrFail('contextFieldsSelectable-0'); + await testSubjects.click('contextFieldsSelectable-0'); await testSubjects.existOrFail('contextField'); const fields = await testSubjects.findAll('contextField'); expect(fields.length).to.be(1); - await testSubjects.click('euiFlyoutCloseButton'); }, }, }; diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 452abe6a54ea5..c9d97825f381c 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -247,18 +247,22 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { log.debug(`Inference endpoint '${inferenceId}' already exists. Nothing to create.`); return; } - const { body, status } = await esSupertest - .put(`/_inference/${taskType}/${inferenceId}`) + const response = await kbnSupertest + .put(`/internal/ml/_inference/${taskType}/${inferenceId}`) + .set(getCommonRequestHeader('1')) .send(requestBody); - this.assertResponseStatusCode(200, status, body); - return body; + this.assertResponseStatusCode(200, response.status, response.body); + log.debug('> Inference endpoint created'); + return response; }, async deleteInferenceEndpoint(inferenceId: string, taskType: string) { const { body, status } = await esSupertest.delete(`/_inference/${taskType}/${inferenceId}`); this.assertResponseStatusCode(200, status, body); - + expect(body) + .to.have.property('acknowledged') + .eql(true, 'Response for delete inference endpoint should be acknowledged'); return body; }, diff --git a/x-pack/test/functional/services/ml/dashboard_embeddables.ts b/x-pack/test/functional/services/ml/dashboard_embeddables.ts index b22622ead61d0..3d88b3b9fd9c8 100644 --- a/x-pack/test/functional/services/ml/dashboard_embeddables.ts +++ b/x-pack/test/functional/services/ml/dashboard_embeddables.ts @@ -114,6 +114,13 @@ export function MachineLearningDashboardEmbeddablesProvider( }); }, + async assertMlSectionExists(expectExist = true) { + await retry.tryForTime(60 * 1000, async () => { + await dashboardAddPanel.clickEditorMenuButton(); + await dashboardAddPanel.verifyEmbeddableFactoryGroupExists('ml', expectExist); + }); + }, + async openAnomalyJobSelectionFlyout( mlEmbeddableType: 'ml_anomaly_swimlane' | 'ml_anomaly_charts' | 'ml_single_metric_viewer' ) { diff --git a/x-pack/test/functional/services/ml/security_common.ts b/x-pack/test/functional/services/ml/security_common.ts index 6952183e7bdef..6d9aee298beaa 100644 --- a/x-pack/test/functional/services/ml/security_common.ts +++ b/x-pack/test/functional/services/ml/security_common.ts @@ -21,6 +21,7 @@ export enum USER { ML_VIEWER_SPACE1 = 'ft_ml_viewer_space1', ML_VIEWER_ALL_SPACES = 'ft_ml_viewer_all_spaces', ML_UNAUTHORIZED = 'ft_ml_unauthorized', + ML_DISABLED = 'ft_ml_disabled', } export function MachineLearningSecurityCommonProvider({ getService }: FtrProviderContext) { @@ -133,6 +134,29 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide elasticsearch: { cluster: [], indices: [], run_as: [] }, kibana: [{ base: [], feature: { discover: ['read'] }, spaces: ['default'] }], }, + { + name: 'ft_ml_disabled', + elasticsearch: { cluster: [], indices: [], run_as: [] }, + kibana: [ + { + base: [], + feature: { + // FIXME: We need permission to save search in Discover to test the data viz embeddable + // change permission back to read once tests are moved out of ML + discover: ['all'], + visualize: ['add'], + dashboard: ['all'], + actions: ['all'], + savedObjectsManagement: ['all'], + advancedSettings: ['all'], + indexPatterns: ['all'], + generalCases: ['all'], + ml: ['none'], + }, + spaces: ['*'], + }, + ], + }, { name: 'ft_all_space_ml_none', elasticsearch: { cluster: [], indices: [], run_as: [] }, @@ -230,6 +254,12 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide password: 'mlu001', roles: ['ft_default_space_ml_none', 'ft_ml_source_readonly'], }, + { + name: 'ft_ml_disabled', + full_name: 'ML Disabled', + password: 'mlud001', + roles: ['ft_ml_disabled'], + }, ]; return { diff --git a/x-pack/test/monitoring_api_integration/fixtures/apm/instance.json b/x-pack/test/monitoring_api_integration/fixtures/apm/instance.json index a8294f495166f..43d078b2b5b40 100644 --- a/x-pack/test/monitoring_api_integration/fixtures/apm/instance.json +++ b/x-pack/test/monitoring_api_integration/fixtures/apm/instance.json @@ -1 +1 @@ -{"metrics":{"apm_cpu":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cpu.total.value","metricAgg":"max","label":"Total","title":"CPU Utilization","description":"Percentage of CPU time spent executing (user+kernel mode) for the APM process","units":"%","format":"0.[00]","hasCalculation":true,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0.6],[1679500660000,0.6],[1679500670000,0.6],[1679500680000,0.7000000000000001],[1679500690000,0.6],[1679500700000,0.8],[1679500710000,1],[1679500720000,0.6],[1679500730000,0.5],[1679500740000,0.8],[1679500750000,0.6],[1679500760000,0.6],[1679500770000,0.6],[1679500780000,1],[1679500790000,0.7000000000000001],[1679500800000,0.5],[1679500810000,0.6],[1679500820000,0.6],[1679500830000,0.5],[1679500840000,0.7000000000000001],[1679500850000,1],[1679500860000,0.7000000000000001],[1679500870000,0.8]]}],"apm_os_load":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.1","metricAgg":"max","label":"1m","title":"System Load","description":"Load average over the last 1 minute","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,2.54],[1679500650000,3.03],[1679500660000,2.71],[1679500670000,2.44],[1679500680000,2.22],[1679500690000,2.03],[1679500700000,2.35],[1679500710000,2.06],[1679500720000,1.98],[1679500730000,1.75],[1679500740000,1.64],[1679500750000,1.39],[1679500760000,1.17],[1679500770000,1.07],[1679500780000,0.91],[1679500790000,1.08],[1679500800000,1.14],[1679500810000,0.96],[1679500820000,0.81],[1679500830000,1.06],[1679500840000,1.05],[1679500850000,1.13],[1679500860000,1.35],[1679500870000,1.14]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.5","metricAgg":"max","label":"5m","title":"System Load","description":"Load average over the last 5 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,2.05],[1679500650000,2.17],[1679500660000,2.13],[1679500670000,2.09],[1679500680000,2.05],[1679500690000,2.02],[1679500700000,2.08],[1679500710000,2.03],[1679500720000,2.01],[1679500730000,1.96],[1679500740000,1.93],[1679500750000,1.87],[1679500760000,1.8],[1679500770000,1.76],[1679500780000,1.7],[1679500790000,1.71],[1679500800000,1.71],[1679500810000,1.65],[1679500820000,1.59],[1679500830000,1.62],[1679500840000,1.6],[1679500850000,1.6],[1679500860000,1.63],[1679500870000,1.57]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.15","metricAgg":"max","label":"15m","title":"System Load","description":"Load average over the last 15 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,1.47],[1679500650000,1.51],[1679500660000,1.5],[1679500670000,1.5],[1679500680000,1.49],[1679500690000,1.49],[1679500700000,1.51],[1679500710000,1.5],[1679500720000,1.5],[1679500730000,1.49],[1679500740000,1.49],[1679500750000,1.47],[1679500760000,1.45],[1679500770000,1.44],[1679500780000,1.43],[1679500790000,1.43],[1679500800000,1.43],[1679500810000,1.42],[1679500820000,1.4],[1679500830000,1.41],[1679500840000,1.41],[1679500850000,1.41],[1679500860000,1.42],[1679500870000,1.41]]}],"apm_memory":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.memory_alloc","metricAgg":"max","label":"Allocated Memory","title":"Memory","description":"Allocated memory","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,37395216],[1679500650000,41615536],[1679500660000,45549872],[1679500670000,48639824],[1679500680000,52503144],[1679500690000,55889632],[1679500700000,59721688],[1679500710000,38234784],[1679500720000,41191920],[1679500730000,45048264],[1679500740000,49664736],[1679500750000,53688584],[1679500760000,57633520],[1679500770000,61293528],[1679500780000,39092008],[1679500790000,43694136],[1679500800000,46895384],[1679500810000,50130024],[1679500820000,53510976],[1679500830000,57166128],[1679500840000,61012360],[1679500850000,39088512],[1679500860000,43863416],[1679500870000,47825208]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.rss","metricAgg":"max","label":"Process Total","title":"Memory","description":"Resident set size of memory reserved by the APM service from the OS","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,62275584],[1679500650000,62472192],[1679500660000,62685184],[1679500670000,65806336],[1679500680000,70074368],[1679500690000,73576448],[1679500700000,77508608],[1679500710000,80875520],[1679500720000,80875520],[1679500730000,80875520],[1679500740000,81080320],[1679500750000,81289216],[1679500760000,81289216],[1679500770000,81289216],[1679500780000,81502208],[1679500790000,81502208],[1679500800000,81502208],[1679500810000,81502208],[1679500820000,81502208],[1679500830000,81502208],[1679500840000,81653760],[1679500850000,81653760],[1679500860000,81653760],[1679500870000,82456576]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,73697880],[1679500650000,73697880],[1679500660000,73697880],[1679500670000,73697880],[1679500680000,73697880],[1679500690000,73697880],[1679500700000,73697880],[1679500710000,74066896],[1679500720000,74066896],[1679500730000,74066896],[1679500740000,74066896],[1679500750000,74066896],[1679500760000,74066896],[1679500770000,74066896],[1679500780000,73968024],[1679500790000,73968024],[1679500800000,73968024],[1679500810000,73968024],[1679500820000,73968024],[1679500830000,73968024],[1679500840000,73968024],[1679500850000,73963784],[1679500860000,73963784],[1679500870000,73963784]]}],"apm_memory_cgroup":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.usage.bytes","metricAgg":"max","label":"Memory Utilization (cgroup)","title":"Memory","description":"Memory usage of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,32874496],[1679500650000,33325056],[1679500660000,33345536],[1679500670000,36667392],[1679500680000,40730624],[1679500690000,44462080],[1679500700000,48246784],[1679500710000,51675136],[1679500720000,51695616],[1679500730000,51720192],[1679500740000,51822592],[1679500750000,52109312],[1679500760000,52174848],[1679500770000,52101120],[1679500780000,52178944],[1679500790000,52215808],[1679500800000,52228096],[1679500810000,52248576],[1679500820000,52264960],[1679500830000,52400128],[1679500840000,52289536],[1679500850000,52518912],[1679500860000,52428800],[1679500870000,52580352]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.limit.bytes","metricAgg":"max","label":"Memory Limit","title":"Memory","description":"Memory limit of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,1073741824],[1679500650000,1073741824],[1679500660000,1073741824],[1679500670000,1073741824],[1679500680000,1073741824],[1679500690000,1073741824],[1679500700000,1073741824],[1679500710000,1073741824],[1679500720000,1073741824],[1679500730000,1073741824],[1679500740000,1073741824],[1679500750000,1073741824],[1679500760000,1073741824],[1679500770000,1073741824],[1679500780000,1073741824],[1679500790000,1073741824],[1679500800000,1073741824],[1679500810000,1073741824],[1679500820000,1073741824],[1679500830000,1073741824],[1679500840000,1073741824],[1679500850000,1073741824],[1679500860000,1073741824],[1679500870000,1073741824]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,73697880],[1679500650000,73697880],[1679500660000,73697880],[1679500670000,73697880],[1679500680000,73697880],[1679500690000,73697880],[1679500700000,73697880],[1679500710000,74066896],[1679500720000,74066896],[1679500730000,74066896],[1679500740000,74066896],[1679500750000,74066896],[1679500760000,74066896],[1679500770000,74066896],[1679500780000,73968024],[1679500790000,73968024],[1679500800000,73968024],[1679500810000,73968024],[1679500820000,73968024],[1679500830000,73968024],[1679500840000,73968024],[1679500850000,73963784],[1679500860000,73963784],[1679500870000,73963784]]}],"apm_output_events_rate_success":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.total","metricAgg":"max","label":"Total","title":"Output Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,8.4],[1679500660000,8.8],[1679500670000,6.8],[1679500680000,7.7],[1679500690000,7.6],[1679500700000,8],[1679500710000,8.4],[1679500720000,6.4],[1679500730000,8.4],[1679500740000,10.5],[1679500750000,8.4],[1679500760000,8.4],[1679500770000,7.6],[1679500780000,8],[1679500790000,10],[1679500800000,6.5],[1679500810000,6.8],[1679500820000,7.2],[1679500830000,7.6],[1679500840000,8.4],[1679500850000,8],[1679500860000,10.5],[1679500870000,9.2]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.active","metricAgg":"max","label":"Active","title":"Output Active Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,null],[1679500660000,0.8],[1679500670000,0],[1679500680000,null],[1679500690000,0.4],[1679500700000,0.4],[1679500710000,0.8],[1679500720000,null],[1679500730000,0.8],[1679500740000,0.4],[1679500750000,null],[1679500760000,0],[1679500770000,null],[1679500780000,0.8],[1679500790000,null],[1679500800000,0],[1679500810000,null],[1679500820000,0],[1679500830000,1.6],[1679500840000,null],[1679500850000,null],[1679500860000,0.8],[1679500870000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.acked","metricAgg":"max","label":"Acked","title":"Output Acked Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,9.2],[1679500660000,8],[1679500670000,6.8],[1679500680000,8.5],[1679500690000,7.2],[1679500700000,7.6],[1679500710000,7.6],[1679500720000,8],[1679500730000,7.6],[1679500740000,10.1],[1679500750000,9.2],[1679500760000,8.4],[1679500770000,8],[1679500780000,7.2],[1679500790000,10.4],[1679500800000,6.5],[1679500810000,7.2],[1679500820000,7.2],[1679500830000,6],[1679500840000,9.6],[1679500850000,8.4],[1679500860000,9.7],[1679500870000,10]]}],"apm_output_events_rate_failure":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.failed","metricAgg":"max","label":"Failed","title":"Output Failed Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.dropped","metricAgg":"max","label":"Dropped","title":"Output Dropped Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_responses_valid":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.count","metricAgg":"max","label":"Total","title":"Response Count Intake API","description":"HTTP Requests responded to by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.ok","metricAgg":"max","label":"Ok","title":"Ok","description":"200 OK response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.accepted","metricAgg":"max","label":"Accepted","title":"Accepted","description":"HTTP Requests successfully reporting new events","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]}],"apm_responses_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.toolarge","metricAgg":"max","label":"Too large","title":"Response Errors Intake API","description":"HTTP Requests rejected due to excessive payload size","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.validate","metricAgg":"max","label":"Validate","title":"Validate","description":"HTTP Requests rejected due to payload validation error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.method","metricAgg":"max","label":"Method","title":"Method","description":"HTTP Requests rejected due to incorrect HTTP method","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.unauthorized","metricAgg":"max","label":"Unauthorized","title":"Unauthorized","description":"HTTP Requests rejected due to invalid secret token","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.ratelimit","metricAgg":"max","label":"Rate limit","title":"Rate limit","description":"HTTP Requests rejected to due excessive rate limit","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.queue","metricAgg":"max","label":"Queue","title":"Queue","description":"HTTP Requests rejected to due internal queue filling up","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.decode","metricAgg":"max","label":"Decode","title":"Decode","description":"HTTP Requests rejected to due decoding errors - invalid json, incorrect data type for entity","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.forbidden","metricAgg":"max","label":"Forbidden","title":"Forbidden","description":"Forbidden HTTP Requests rejected - CORS violation, disabled enpoint","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.concurrency","metricAgg":"max","label":"Concurrency","title":"Concurrency","description":"HTTP Requests rejected due to overall concurrency limit breach","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.closed","metricAgg":"max","label":"Closed","title":"Closed","description":"HTTP Requests rejected during server shutdown","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.internal","metricAgg":"max","label":"Internal","title":"Internal","description":"HTTP Requests rejected due to a miscellaneous internal error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_requests":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.request.count","metricAgg":"max","label":"Requested","title":"Request Count Intake API","description":"HTTP Requests received by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]}],"apm_transformations":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.transaction.transformations","metricAgg":"max","label":"Transaction","title":"Processed Events","description":"Transaction events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.span.transformations","metricAgg":"max","label":"Span","title":"Transformations","description":"Span events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.error.transformations","metricAgg":"max","label":"Error","title":"Transformations","description":"Error events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.metric.transformations","metricAgg":"max","label":"Metric","title":"Transformations","description":"Metric events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,2.3],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,3],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,2],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,3],[1679500870000,2.3]]}],"apm_acm_response":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.count","metricAgg":"max","label":"Count","title":"Response Count Agent Configuration Management","description":"HTTP requests responded to by APM Server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.count","metricAgg":"max","label":"Error Count","title":"Response Error Count Agent Configuration Management","description":"HTTP errors count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.valid.ok","metricAgg":"max","label":"OK","title":"Response OK Count Agent Configuration Management","description":"200 OK response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.valid.notmodified","metricAgg":"max","label":"Not Modified","title":"Response Not Modified Agent Configuration Management","description":"304 Not modified response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_acm_response_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.forbidden","metricAgg":"max","label":"Count","title":"Response Errors Agent Configuration Management","description":"Forbidden HTTP requests rejected count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.unauthorized","metricAgg":"max","label":"Unauthorized","title":"Response Unauthorized Errors Agent Configuration Management","description":"Unauthorized HTTP requests rejected count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.unavailable","metricAgg":"max","label":"Unavailable","title":"Response Unavailable Errors Agent Configuration Management","description":"Unavailable HTTP response count. Possible misconfiguration or unsupported version of Kibana","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.method","metricAgg":"max","label":"Method","title":"Response Method Errors Agent Configuration Management","description":"HTTP requests rejected due to incorrect HTTP method","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.invalidquery","metricAgg":"max","label":"Invalid Query","title":"Response Invalid Query Errors Agent Configuration Management","description":"Invalid HTTP query","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_acm_request_count":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.request.count","metricAgg":"max","label":"Count","title":"Requests Agent Configuration Management","description":"HTTP Requests received by agent configuration managemen","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}]},"apmSummary":{"uuid":"ccf6c4d9-37bc-4dd0-bf57-2ae3ee296a39","transportAddress":"3031a4a32eea","version":"8.7.0","name":"3031a4a32eea","type":"Apm-server","output":"Elasticsearch","configReloads":null,"uptime":271879,"eventsTotal":2020,"eventsEmitted":null,"eventsDropped":null,"bytesWritten":739360,"config":{"container":false},"timeOfLastEvent":"2023-03-22T16:01:37.064Z"}} +{"metrics":{"apm_cpu":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cpu.total.value","metricAgg":"max","label":"Total","title":"CPU Utilization","description":"Percentage of CPU time spent executing (user+kernel mode) for the APM process","units":"%","format":"0.[00]","hasCalculation":true,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0.6],[1679500660000,0.6],[1679500670000,0.6],[1679500680000,0.7000000000000001],[1679500690000,0.6],[1679500700000,0.8],[1679500710000,1],[1679500720000,0.6],[1679500730000,0.5],[1679500740000,0.8],[1679500750000,0.6],[1679500760000,0.6],[1679500770000,0.6],[1679500780000,1],[1679500790000,0.7000000000000001],[1679500800000,0.5],[1679500810000,0.6],[1679500820000,0.6],[1679500830000,0.5],[1679500840000,0.7000000000000001],[1679500850000,1],[1679500860000,0.7000000000000001],[1679500870000,0.8]]}],"apm_os_load":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.1","metricAgg":"max","label":"1m","title":"System Load","description":"Load average over the last 1 minute","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,2.54],[1679500650000,3.03],[1679500660000,2.71],[1679500670000,2.44],[1679500680000,2.22],[1679500690000,2.03],[1679500700000,2.35],[1679500710000,2.06],[1679500720000,1.98],[1679500730000,1.75],[1679500740000,1.64],[1679500750000,1.39],[1679500760000,1.17],[1679500770000,1.07],[1679500780000,0.91],[1679500790000,1.08],[1679500800000,1.14],[1679500810000,0.96],[1679500820000,0.81],[1679500830000,1.06],[1679500840000,1.05],[1679500850000,1.13],[1679500860000,1.35],[1679500870000,1.14]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.5","metricAgg":"max","label":"5m","title":"System Load","description":"Load average over the last 5 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,2.05],[1679500650000,2.17],[1679500660000,2.13],[1679500670000,2.09],[1679500680000,2.05],[1679500690000,2.02],[1679500700000,2.08],[1679500710000,2.03],[1679500720000,2.01],[1679500730000,1.96],[1679500740000,1.93],[1679500750000,1.87],[1679500760000,1.8],[1679500770000,1.76],[1679500780000,1.7],[1679500790000,1.71],[1679500800000,1.71],[1679500810000,1.65],[1679500820000,1.59],[1679500830000,1.62],[1679500840000,1.6],[1679500850000,1.6],[1679500860000,1.63],[1679500870000,1.57]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.15","metricAgg":"max","label":"15m","title":"System Load","description":"Load average over the last 15 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,1.47],[1679500650000,1.51],[1679500660000,1.5],[1679500670000,1.5],[1679500680000,1.49],[1679500690000,1.49],[1679500700000,1.51],[1679500710000,1.5],[1679500720000,1.5],[1679500730000,1.49],[1679500740000,1.49],[1679500750000,1.47],[1679500760000,1.45],[1679500770000,1.44],[1679500780000,1.43],[1679500790000,1.43],[1679500800000,1.43],[1679500810000,1.42],[1679500820000,1.4],[1679500830000,1.41],[1679500840000,1.41],[1679500850000,1.41],[1679500860000,1.42],[1679500870000,1.41]]}],"apm_memory":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.memory_alloc","metricAgg":"max","label":"Allocated Memory","title":"Memory","description":"Allocated memory","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,37395216],[1679500650000,41615536],[1679500660000,45549872],[1679500670000,48639824],[1679500680000,52503144],[1679500690000,55889632],[1679500700000,59721688],[1679500710000,38234784],[1679500720000,41191920],[1679500730000,45048264],[1679500740000,49664736],[1679500750000,53688584],[1679500760000,57633520],[1679500770000,61293528],[1679500780000,39092008],[1679500790000,43694136],[1679500800000,46895384],[1679500810000,50130024],[1679500820000,53510976],[1679500830000,57166128],[1679500840000,61012360],[1679500850000,39088512],[1679500860000,43863416],[1679500870000,47825208]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.rss","metricAgg":"max","label":"Process Total","title":"Memory","description":"Resident set size of memory reserved by the APM service from the OS","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,62275584],[1679500650000,62472192],[1679500660000,62685184],[1679500670000,65806336],[1679500680000,70074368],[1679500690000,73576448],[1679500700000,77508608],[1679500710000,80875520],[1679500720000,80875520],[1679500730000,80875520],[1679500740000,81080320],[1679500750000,81289216],[1679500760000,81289216],[1679500770000,81289216],[1679500780000,81502208],[1679500790000,81502208],[1679500800000,81502208],[1679500810000,81502208],[1679500820000,81502208],[1679500830000,81502208],[1679500840000,81653760],[1679500850000,81653760],[1679500860000,81653760],[1679500870000,82456576]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,73697880],[1679500650000,73697880],[1679500660000,73697880],[1679500670000,73697880],[1679500680000,73697880],[1679500690000,73697880],[1679500700000,73697880],[1679500710000,74066896],[1679500720000,74066896],[1679500730000,74066896],[1679500740000,74066896],[1679500750000,74066896],[1679500760000,74066896],[1679500770000,74066896],[1679500780000,73968024],[1679500790000,73968024],[1679500800000,73968024],[1679500810000,73968024],[1679500820000,73968024],[1679500830000,73968024],[1679500840000,73968024],[1679500850000,73963784],[1679500860000,73963784],[1679500870000,73963784]]}],"apm_memory_cgroup":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.usage.bytes","metricAgg":"max","label":"Memory Utilization (cgroup)","title":"Memory","description":"Memory usage of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,32874496],[1679500650000,33325056],[1679500660000,33345536],[1679500670000,36667392],[1679500680000,40730624],[1679500690000,44462080],[1679500700000,48246784],[1679500710000,51675136],[1679500720000,51695616],[1679500730000,51720192],[1679500740000,51822592],[1679500750000,52109312],[1679500760000,52174848],[1679500770000,52101120],[1679500780000,52178944],[1679500790000,52215808],[1679500800000,52228096],[1679500810000,52248576],[1679500820000,52264960],[1679500830000,52400128],[1679500840000,52289536],[1679500850000,52518912],[1679500860000,52428800],[1679500870000,52580352]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.limit.bytes","metricAgg":"max","label":"Memory Limit","title":"Memory","description":"Memory limit of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,1073741824],[1679500650000,1073741824],[1679500660000,1073741824],[1679500670000,1073741824],[1679500680000,1073741824],[1679500690000,1073741824],[1679500700000,1073741824],[1679500710000,1073741824],[1679500720000,1073741824],[1679500730000,1073741824],[1679500740000,1073741824],[1679500750000,1073741824],[1679500760000,1073741824],[1679500770000,1073741824],[1679500780000,1073741824],[1679500790000,1073741824],[1679500800000,1073741824],[1679500810000,1073741824],[1679500820000,1073741824],[1679500830000,1073741824],[1679500840000,1073741824],[1679500850000,1073741824],[1679500860000,1073741824],[1679500870000,1073741824]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1679500640000,73697880],[1679500650000,73697880],[1679500660000,73697880],[1679500670000,73697880],[1679500680000,73697880],[1679500690000,73697880],[1679500700000,73697880],[1679500710000,74066896],[1679500720000,74066896],[1679500730000,74066896],[1679500740000,74066896],[1679500750000,74066896],[1679500760000,74066896],[1679500770000,74066896],[1679500780000,73968024],[1679500790000,73968024],[1679500800000,73968024],[1679500810000,73968024],[1679500820000,73968024],[1679500830000,73968024],[1679500840000,73968024],[1679500850000,73963784],[1679500860000,73963784],[1679500870000,73963784]]}],"apm_output_events_rate_success":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.total","metricAgg":"max","label":"Total","title":"Output Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,8.4],[1679500660000,8.8],[1679500670000,6.8],[1679500680000,7.7],[1679500690000,7.6],[1679500700000,8],[1679500710000,8.4],[1679500720000,6.4],[1679500730000,8.4],[1679500740000,10.5],[1679500750000,8.4],[1679500760000,8.4],[1679500770000,7.6],[1679500780000,8],[1679500790000,10],[1679500800000,6.5],[1679500810000,6.8],[1679500820000,7.2],[1679500830000,7.6],[1679500840000,8.4],[1679500850000,8],[1679500860000,10.5],[1679500870000,9.2]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.active","metricAgg":"max","label":"Active","title":"Output Active Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,null],[1679500660000,0.8],[1679500670000,0],[1679500680000,null],[1679500690000,0.4],[1679500700000,0.4],[1679500710000,0.8],[1679500720000,null],[1679500730000,0.8],[1679500740000,0.4],[1679500750000,null],[1679500760000,0],[1679500770000,null],[1679500780000,0.8],[1679500790000,null],[1679500800000,0],[1679500810000,null],[1679500820000,0],[1679500830000,1.6],[1679500840000,null],[1679500850000,null],[1679500860000,0.8],[1679500870000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.acked","metricAgg":"max","label":"Acked","title":"Output Acked Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,9.2],[1679500660000,8],[1679500670000,6.8],[1679500680000,8.5],[1679500690000,7.2],[1679500700000,7.6],[1679500710000,7.6],[1679500720000,8],[1679500730000,7.6],[1679500740000,10.1],[1679500750000,9.2],[1679500760000,8.4],[1679500770000,8],[1679500780000,7.2],[1679500790000,10.4],[1679500800000,6.5],[1679500810000,7.2],[1679500820000,7.2],[1679500830000,6],[1679500840000,9.6],[1679500850000,8.4],[1679500860000,9.7],[1679500870000,10]]}],"apm_output_events_rate_failure":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.failed","metricAgg":"max","label":"Failed","title":"Output Failed Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.dropped","metricAgg":"max","label":"Dropped","title":"Output Dropped Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_responses_valid":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.count","metricAgg":"max","label":"Total","title":"Response Count Intake API","description":"HTTP Requests responded to by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.ok","metricAgg":"max","label":"Ok","title":"Ok","description":"200 OK response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.accepted","metricAgg":"max","label":"Accepted","title":"Accepted","description":"HTTP Requests successfully reporting new events","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]}],"apm_responses_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.toolarge","metricAgg":"max","label":"Too large","title":"Response Errors Intake API","description":"HTTP Requests rejected due to excessive payload size","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.validate","metricAgg":"max","label":"Validate","title":"Validate","description":"HTTP Requests rejected due to payload validation error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.method","metricAgg":"max","label":"Method","title":"Method","description":"HTTP Requests rejected due to incorrect HTTP method","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.unauthorized","metricAgg":"max","label":"Unauthorized","title":"Unauthorized","description":"HTTP Requests rejected due to invalid secret token","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.ratelimit","metricAgg":"max","label":"Rate limit","title":"Rate limit","description":"HTTP Requests rejected to due excessive rate limit","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.queue","metricAgg":"max","label":"Queue","title":"Queue","description":"HTTP Requests rejected to due internal queue filling up","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.decode","metricAgg":"max","label":"Decode","title":"Decode","description":"HTTP Requests rejected to due decoding errors - invalid json, incorrect data type for entity","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.forbidden","metricAgg":"max","label":"Forbidden","title":"Forbidden","description":"Forbidden HTTP Requests rejected - CORS violation, disabled enpoint","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.concurrency","metricAgg":"max","label":"Concurrency","title":"Concurrency","description":"HTTP Requests rejected due to overall concurrency limit breach","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.closed","metricAgg":"max","label":"Closed","title":"Closed","description":"HTTP Requests rejected during server shutdown","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.internal","metricAgg":"max","label":"Internal","title":"Internal","description":"HTTP Requests rejected due to a miscellaneous internal error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_requests":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.request.count","metricAgg":"max","label":"Requested","title":"Request Count Intake API","description":"HTTP Requests received by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]}],"apm_transformations":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.transaction.transformations","metricAgg":"max","label":"Transaction","title":"Processed Events","description":"Transaction events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.span.transformations","metricAgg":"max","label":"Span","title":"Transformations","description":"Span events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.error.transformations","metricAgg":"max","label":"Error","title":"Transformations","description":"Error events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,1.8],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,2.5],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,1.5],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,2.5],[1679500870000,2.3]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.metric.transformations","metricAgg":"max","label":"Metric","title":"Transformations","description":"Metric events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,2.1],[1679500660000,2.2],[1679500670000,1.7],[1679500680000,2.3],[1679500690000,1.9],[1679500700000,2],[1679500710000,2.1],[1679500720000,1.6],[1679500730000,2.1],[1679500740000,3],[1679500750000,2.1],[1679500760000,2.1],[1679500770000,1.9],[1679500780000,2],[1679500790000,2.5],[1679500800000,2],[1679500810000,1.7],[1679500820000,1.8],[1679500830000,1.9],[1679500840000,2.1],[1679500850000,2],[1679500860000,3],[1679500870000,2.3]]}],"apm_acm_response":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.count","metricAgg":"max","label":"Count","title":"Response Count Agent Configuration Management","description":"HTTP requests responded to by APM Server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.count","metricAgg":"max","label":"Error Count","title":"Response Error Count Agent Configuration Management","description":"HTTP errors count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.valid.ok","metricAgg":"max","label":"OK","title":"Response OK Count Agent Configuration Management","description":"200 OK response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.valid.notmodified","metricAgg":"max","label":"Not Modified","title":"Response Not Modified Agent Configuration Management","description":"304 Not modified response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_acm_response_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.forbidden","metricAgg":"max","label":"Count","title":"Response Errors Agent Configuration Management","description":"Forbidden HTTP requests rejected count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.unauthorized","metricAgg":"max","label":"Unauthorized","title":"Response Unauthorized Errors Agent Configuration Management","description":"Unauthorized HTTP requests rejected count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.unavailable","metricAgg":"max","label":"Unavailable","title":"Response Unavailable Errors Agent Configuration Management","description":"Unavailable HTTP response count. Possible misconfiguration or unsupported version of Kibana","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.method","metricAgg":"max","label":"Method","title":"Response Method Errors Agent Configuration Management","description":"HTTP requests rejected due to incorrect HTTP method","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.invalidquery","metricAgg":"max","label":"Invalid Query","title":"Response Invalid Query Errors Agent Configuration Management","description":"Invalid HTTP query","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}],"apm_acm_request_count":[{"bucket_size":"10 seconds","timeRange":{"min":1679500640000,"max":1679500880000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.request.count","metricAgg":"max","label":"Count","title":"Requests Agent Configuration Management","description":"HTTP Requests received by agent configuration management","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1679500640000,null],[1679500650000,0],[1679500660000,0],[1679500670000,0],[1679500680000,0],[1679500690000,0],[1679500700000,0],[1679500710000,0],[1679500720000,0],[1679500730000,0],[1679500740000,0],[1679500750000,0],[1679500760000,0],[1679500770000,0],[1679500780000,0],[1679500790000,0],[1679500800000,0],[1679500810000,0],[1679500820000,0],[1679500830000,0],[1679500840000,0],[1679500850000,0],[1679500860000,0],[1679500870000,0]]}]},"apmSummary":{"uuid":"ccf6c4d9-37bc-4dd0-bf57-2ae3ee296a39","transportAddress":"3031a4a32eea","version":"8.7.0","name":"3031a4a32eea","type":"Apm-server","output":"Elasticsearch","configReloads":null,"uptime":271879,"eventsTotal":2020,"eventsEmitted":null,"eventsDropped":null,"bytesWritten":739360,"config":{"container":false},"timeOfLastEvent":"2023-03-22T16:01:37.064Z"}} diff --git a/x-pack/test/security_api_integration/tests/audit/audit_log.ts b/x-pack/test/security_api_integration/tests/audit/audit_log.ts index fba60de2ffadd..e8c5d03bfc562 100644 --- a/x-pack/test/security_api_integration/tests/audit/audit_log.ts +++ b/x-pack/test/security_api_integration/tests/audit/audit_log.ts @@ -23,23 +23,27 @@ export default function ({ getService }: FtrProviderContext) { await logFile.reset(); }); - // FLAKY: https://github.com/elastic/kibana/issues/119267 - it.skip('logs audit events when reading and writing saved objects', async () => { + it('logs audit events when reading and writing saved objects', async () => { await supertest.get('/audit_log?query=param').set('kbn-xsrf', 'foo').expect(204); await logFile.isWritten(); const content = await logFile.readJSON(); - const httpEvent = content.find((c) => c.event.action === 'http_request'); + const httpEvent = content.find( + (c) => c.event.action === 'http_request' && c.url.path === '/audit_log' + ); expect(httpEvent).to.be.ok(); expect(httpEvent.trace.id).to.be.ok(); expect(httpEvent.user.name).to.be(username); expect(httpEvent.kibana.space_id).to.be('default'); expect(httpEvent.http.request.method).to.be('get'); - expect(httpEvent.url.path).to.be('/audit_log'); expect(httpEvent.url.query).to.be('query=param'); - const createEvent = content.find((c) => c.event.action === 'saved_object_create'); + const createEvent = content.find( + (c) => + c.event.action === 'saved_object_create' && c.kibana.saved_object.type === 'dashboard' + ); + expect(createEvent).to.be.ok(); expect(createEvent.trace.id).to.be.ok(); expect(createEvent.user.name).to.be(username); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_rule_exceptions_ess.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_rule_exceptions_ess.ts index 9406964106170..a1708cf4d6ae0 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_rule_exceptions_ess.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/create_rule_exceptions_ess.ts @@ -14,7 +14,6 @@ import { getRuleSOById, createRuleThroughAlertingEndpoint, getRuleSavedObjectWithLegacyInvestigationFields, - checkInvestigationFieldSoValue, } from '../../../../utils'; import { createAlertsIndex, @@ -79,25 +78,15 @@ export default ({ getService }: FtrProviderContext) => { }) .expect(200); - /** - * Confirm type on SO so that it's clear in the tests whether it's expected that - * the SO itself is migrated to the inteded object type, or if the transformation is - * happening just on the response. In this case, change will - * NOT include a migration on SO. - */ const { hits: { hits: [{ _source: ruleSO }], }, } = await getRuleSOById(es, ruleWithLegacyInvestigationField.id); - const isInvestigationFieldMigratedInSo = await checkInvestigationFieldSoValue(ruleSO, { - field_names: ['client.address', 'agent.name'], - }); expect( ruleSO?.alert.params.exceptionsList.some((list) => list.type === 'rule_default') ).to.eql(true); - expect(isInvestigationFieldMigratedInSo).to.eql(false); }); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_bulk.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_bulk.ts index 7e496ea73194d..947b191469e3d 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_bulk.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_bulk.ts @@ -532,7 +532,7 @@ export default ({ getService }: FtrProviderContext) => { ); }); - it('should patch a rule with a legacy investigation field and transform field in response', async () => { + it('should patch a rule with a legacy investigation field and migrate field', async () => { // patch a simple rule's name const { body } = await securitySolutionApi .bulkPatchRules({ @@ -548,19 +548,13 @@ export default ({ getService }: FtrProviderContext) => { }); expect(bodyToCompareLegacyField.name).to.eql('some other name'); - /** - * Confirm type on SO so that it's clear in the tests whether it's expected that - * the SO itself is migrated to the inteded object type, or if the transformation is - * happening just on the response. In this case, change should - * NOT include a migration on SO. - */ const isInvestigationFieldMigratedInSo = await checkInvestigationFieldSoValue( undefined, { field_names: ['client.address', 'agent.name'] }, es, body[0].id ); - expect(isInvestigationFieldMigratedInSo).to.eql(false); + expect(isInvestigationFieldMigratedInSo).to.eql(true); }); it('should patch a rule with a legacy investigation field - empty array - and transform field in response', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_ess.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_ess.ts index 30398cd2cd1e9..d28358519e307 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_ess.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules_ess.ts @@ -137,7 +137,7 @@ export default ({ getService }: FtrProviderContext) => { ); }); - it('should patch a rule with a legacy investigation field and transform response', async () => { + it('should patch a rule with a legacy investigation field and migrate field', async () => { const { body } = await supertest .patch(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') @@ -152,12 +152,7 @@ export default ({ getService }: FtrProviderContext) => { expect(bodyToCompare.investigation_fields).to.eql({ field_names: ['client.address', 'agent.name'], }); - /** - * Confirm type on SO so that it's clear in the tests whether it's expected that - * the SO itself is migrated to the inteded object type, or if the transformation is - * happening just on the response. In this case, change should - * NOT include a migration on SO. - */ + const isInvestigationFieldMigratedInSo = await checkInvestigationFieldSoValue( undefined, { @@ -166,7 +161,7 @@ export default ({ getService }: FtrProviderContext) => { es, body.id ); - expect(isInvestigationFieldMigratedInSo).to.eql(false); + expect(isInvestigationFieldMigratedInSo).to.eql(true); }); it('should patch a rule with a legacy investigation field - empty array - and transform response', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint_api_int/apis/endpoint_authz.ts b/x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint_api_int/apis/endpoint_authz.ts index 3f69edf091707..a9aa7af829225 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint_api_int/apis/endpoint_authz.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/security_solution_endpoint_api_int/apis/endpoint_authz.ts @@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) { method: keyof Pick; path: string; version?: string; - body: Record | undefined; + body: Record | (() => Record) | undefined; } describe('@ess @serverless When attempting to call an endpoint api', function () { @@ -92,13 +92,13 @@ export default function ({ getService }: FtrProviderContext) { { method: 'post', path: ISOLATE_HOST_ROUTE_V2, - body: { endpoint_ids: ['one'] }, + body: () => ({ endpoint_ids: [agentId] }), version: '2023-10-31', }, { method: 'post', path: UNISOLATE_HOST_ROUTE_V2, - body: { endpoint_ids: ['one'] }, + body: () => ({ endpoint_ids: [agentId] }), version: '2023-10-31', }, ]; @@ -107,19 +107,19 @@ export default function ({ getService }: FtrProviderContext) { { method: 'post', path: GET_PROCESSES_ROUTE, - body: { endpoint_ids: ['one'] }, + body: () => ({ endpoint_ids: [agentId] }), version: '2023-10-31', }, { method: 'post', path: KILL_PROCESS_ROUTE, - body: { endpoint_ids: ['one'], parameters: { entity_id: 'abc123' } }, + body: () => ({ endpoint_ids: [agentId], parameters: { entity_id: 'abc123' } }), version: '2023-10-31', }, { method: 'post', path: SUSPEND_PROCESS_ROUTE, - body: { endpoint_ids: ['one'], parameters: { entity_id: 'abc123' } }, + body: () => ({ endpoint_ids: [agentId], parameters: { entity_id: 'abc123' } }), version: '2023-10-31', }, ]; @@ -128,7 +128,7 @@ export default function ({ getService }: FtrProviderContext) { { method: 'post', path: GET_FILE_ROUTE, - body: { endpoint_ids: ['one'], parameters: { path: '/opt/file/doc.txt' } }, + body: () => ({ endpoint_ids: [agentId], parameters: { path: '/opt/file/doc.txt' } }), version: '2023-10-31', }, ]; @@ -138,7 +138,7 @@ export default function ({ getService }: FtrProviderContext) { method: 'post', path: EXECUTE_ROUTE, version: '2023-10-31', - body: { endpoint_ids: ['one'], parameters: { command: 'ls -la' } }, + body: () => ({ endpoint_ids: [agentId], parameters: { command: 'ls -la' } }), }, ]; @@ -155,6 +155,10 @@ export default function ({ getService }: FtrProviderContext) { return path.replace('{action_id}', actionId).replace('{agentId}', agentId); } + function getBodyPayload(apiCall: ApiCallsInterface): ApiCallsInterface['body'] { + return typeof apiCall.body === 'function' ? apiCall.body() : apiCall.body; + } + before(async () => { indexedData = await endpointTestResources.loadEndpointData(); agentId = indexedData.hosts[0].agent.id; @@ -179,7 +183,7 @@ export default function ({ getService }: FtrProviderContext) { .auth(ROLE.t1_analyst, 'changeme') .set('kbn-xsrf', 'xxx') .set(apiListItem.version ? 'Elastic-Api-Version' : 'foo', '2023-10-31') - .send(apiListItem.body) + .send(getBodyPayload(apiListItem)) .expect(403, { statusCode: 403, error: 'Forbidden', @@ -196,7 +200,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[apiListItem.method](replacePathIds(apiListItem.path)) .auth(ROLE.t1_analyst, 'changeme') .set('kbn-xsrf', 'xxx') - .send(apiListItem.body) + .send(getBodyPayload(apiListItem)) .expect(200); }); } @@ -214,7 +218,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[apiListItem.method](replacePathIds(apiListItem.path)) .auth(ROLE.platform_engineer, 'changeme') .set('kbn-xsrf', 'xxx') - .send(apiListItem.body) + .send(getBodyPayload(apiListItem)) .expect(403, { statusCode: 403, error: 'Forbidden', @@ -234,7 +238,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[apiListItem.method](replacePathIds(apiListItem.path)) .auth(ROLE.platform_engineer, 'changeme') .set('kbn-xsrf', 'xxx') - .send(apiListItem.body) + .send(getBodyPayload(apiListItem)) .expect(200); }); } @@ -248,7 +252,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[apiListItem.method](replacePathIds(apiListItem.path)) .auth(ROLE.endpoint_operations_analyst, 'changeme') .set('kbn-xsrf', 'xxx') - .send(apiListItem.body) + .send(getBodyPayload(apiListItem)) .expect(403, { statusCode: 403, error: 'Forbidden', @@ -270,7 +274,7 @@ export default function ({ getService }: FtrProviderContext) { await supertestWithoutAuth[apiListItem.method](replacePathIds(apiListItem.path)) .auth(ROLE.endpoint_operations_analyst, 'changeme') .set('kbn-xsrf', 'xxx') - .send(apiListItem.body) + .send(getBodyPayload(apiListItem)) .expect(200); }); } @@ -291,7 +295,7 @@ export default function ({ getService }: FtrProviderContext) { }]`, async () => { await supertest[apiListItem.method](replacePathIds(apiListItem.path)) .set('kbn-xsrf', 'xxx') - .send(apiListItem.body) + .send(getBodyPayload(apiListItem)) .expect(200); }); } diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index cda6e59087262..2a97ed4bcc9c0 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -171,6 +171,7 @@ "@kbn/alerting-comparators", "@kbn/alerting-state-types", "@kbn/reporting-server", - "@kbn/data-quality-plugin" + "@kbn/data-quality-plugin", + "@kbn/ml-trained-models-utils" ] } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts index b5f51258c8df4..5e0a5baedb280 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/datastreams.ts @@ -23,7 +23,7 @@ export default function ({ getService }: FtrProviderContext) { describe('Data streams', function () { // see details: https://github.com/elastic/kibana/issues/187372 - this.tags(['skipSvlSec']); + this.tags(['failsOnMKI']); before(async () => { roleAuthc = await svlUserManager.createApiKeyForRole('admin'); internalReqHeader = svlCommonApi.getInternalRequestHeader(); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts index 3a075c8546660..e52ae73a548bb 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -17,6 +18,8 @@ export default function ({ getService }: FtrProviderContext) { const inferenceId = 'my-elser-model'; const taskType = 'sparse_embedding'; const service = 'elser'; + + const modelId = '.elser_model_2'; const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); const supertestWithoutAuth = getService('supertestWithoutAuth'); @@ -24,50 +27,52 @@ export default function ({ getService }: FtrProviderContext) { let internalReqHeader: InternalRequestHeader; // FLAKY: https://github.com/elastic/kibana/issues/185216 - describe.skip('Inference endpoints', function () { - // test adds new trained model '.elser_model_2_linux-x86_64', but does not clean it. Follow up tests are affected - this.tags(['failsOnMKI']); + describe('Inference endpoints', function () { before(async () => { roleAuthc = await svlUserManager.createApiKeyForRole('admin'); internalReqHeader = svlCommonApi.getInternalRequestHeader(); - log.debug(`Creating inference endpoint`); - try { - await ml.api.createInferenceEndpoint(inferenceId, taskType, { - service, - service_settings: { - num_allocations: 1, - num_threads: 1, - }, - }); - } catch (err) { - log.debug('[Setup error] Error creating inference endpoint'); - throw err; - } }); - after(async () => { - // Cleanup inference endpoints created for testing purposes try { - log.debug(`Deleting inference endpoint`); - await ml.api.deleteInferenceEndpoint(inferenceId, taskType); + log.debug(`Deleting underlying trained model`); + await ml.api.deleteTrainedModelES(modelId); + await ml.testResources.cleanMLSavedObjects(); } catch (err) { - log.debug('[Cleanup error] Error deleting inference endpoint'); + log.debug('[Cleanup error] Error deleting trained model and saved ml objects'); throw err; } await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); - describe('get inference endpoints', () => { - it('returns the existing inference endpoints', async () => { - const { body: inferenceEndpoints } = await supertestWithoutAuth - .get(`${API_BASE_PATH}/inference/all`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(inferenceEndpoints).to.be.ok(); - expect(inferenceEndpoints[0].model_id).to.eql(inferenceId); + it('create inference endpoint', async () => { + log.debug(`create inference endpoint`); + await ml.api.createInferenceEndpoint(inferenceId, taskType, { + service, + service_settings: { + num_allocations: 1, + num_threads: 1, + model_id: modelId, + }, }); }); + it('get all inference endpoints and confirm inference endpoint exist', async () => { + const { body: inferenceEndpoints } = await supertestWithoutAuth + .get(`${API_BASE_PATH}/inference/all`) + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(inferenceEndpoints).to.be.ok(); + expect( + inferenceEndpoints.some( + (endpoint: InferenceAPIConfigResponse) => endpoint.model_id === inferenceId + ) + ).to.be(true); + }); + it('can delete inference endpoint', async () => { + log.debug(`Deleting inference endpoint`); + await ml.api.deleteInferenceEndpoint(inferenceId, taskType); + log.debug('> Inference endpoint deleted'); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/index.ts b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/index.ts index 460f4ac43bb43..f3bc56e16a5ae 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/index.ts @@ -17,6 +17,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./benchmark/v2')); loadTestFile(require.resolve('./find_csp_benchmark_rule')); loadTestFile(require.resolve('./telemetry')); + loadTestFile(require.resolve('./serverless_metering/cloud_security_metering')); // TODO: migrate status_unprivileged tests from stateful, if it feasible in serverless with the new security model // loadTestFile(require.resolve('./status/status_unprivileged')); diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/cloud_security_metering.ts b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/cloud_security_metering.ts new file mode 100644 index 0000000000000..78f7ae579d318 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/cloud_security_metering.ts @@ -0,0 +1,363 @@ +/* + * 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 expect from '@kbn/expect'; +import { + LATEST_FINDINGS_INDEX_DEFAULT_NS, + LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, +} from '@kbn/cloud-security-posture-plugin/common/constants'; +import * as http from 'http'; +import { RoleCredentials } from '../../../../../shared/services'; +import { getMockFindings, getMockDefendForContainersHeartbeats } from './mock_data'; // eslint-disable-line @kbn/imports/no_boundary_crossing +import type { FtrProviderContext } from '../../../../ftr_provider_context'; +import { + deleteIndex, + addIndex, + createPackagePolicy, + createCloudDefendPackagePolicy, +} from '../../../../../../test/api_integration/apis/cloud_security_posture/helper'; // eslint-disable-line @kbn/imports/no_boundary_crossing +import { UsageRecord, getInterceptedRequestPayload, setupMockServer } from './mock_usage_server'; // eslint-disable-line @kbn/imports/no_boundary_crossing + +const CLOUD_DEFEND_HEARTBEAT_INDEX_DEFAULT_NS = 'metrics-cloud_defend.heartbeat-default'; + +export default function (providerContext: FtrProviderContext) { + const mockUsageApiApp = setupMockServer(); + const { getService } = providerContext; + const retry = getService('retry'); + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + /* + This test aims to intercept the usage API request sent by the metering background task manager. + The task manager is running by default in security serverless project in the background and sending usage API requests to the usage API. + This test mocks the usage API server and intercepts the usage API request sent by the metering background task manager. + */ + describe('Intercept the usage API request sent by the metering background task manager', function () { + this.tags(['skipMKI']); + + let mockUsageApiServer: http.Server; + let agentPolicyId: string; + let roleAuthc: RoleCredentials; + let internalRequestHeader: { 'x-elastic-internal-origin': string; 'kbn-xsrf': string }; + before(async () => { + mockUsageApiServer = mockUsageApiApp.listen(8081); // Start the usage api mock server on port 8081 + }); + + beforeEach(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + internalRequestHeader = svlCommonApi.getInternalRequestHeader(); + + await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + + const { body: agentPolicyResponse } = await supertestWithoutAuth + .post(`/api/fleet/agent_policies`) + .set(internalRequestHeader) + .set(roleAuthc.apiKeyHeader) + .send({ + name: 'Test policy', + namespace: 'default', + }); + + agentPolicyId = agentPolicyResponse.item.id; + + await deleteIndex(es, [ + LATEST_FINDINGS_INDEX_DEFAULT_NS, + LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, + CLOUD_DEFEND_HEARTBEAT_INDEX_DEFAULT_NS, + ]); + }); + + afterEach(async () => { + await deleteIndex(es, [ + LATEST_FINDINGS_INDEX_DEFAULT_NS, + LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, + ]); + await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await deleteIndex(es, [ + LATEST_FINDINGS_INDEX_DEFAULT_NS, + LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, + CLOUD_DEFEND_HEARTBEAT_INDEX_DEFAULT_NS, + ]); + }); + after(async () => { + await svlUserManager.invalidateApiKeyForRole(roleAuthc); + mockUsageApiServer.close(); + }); + + it('Should intercept usage API request for CSPM', async () => { + await createPackagePolicy( + supertestWithoutAuth, + agentPolicyId, + 'cspm', + 'cloudbeat/cis_aws', + 'aws', + 'cspm', + 'CSPM-1', + roleAuthc, + internalRequestHeader + ); + const billableFindings = getMockFindings({ + postureType: 'cspm', + isBillableAsset: true, + numberOfFindings: 5, + }); + + const notBillableFindings = getMockFindings({ + postureType: 'cspm', + isBillableAsset: false, + numberOfFindings: 10, + }); + + await addIndex( + es, + [...billableFindings, ...notBillableFindings], + LATEST_FINDINGS_INDEX_DEFAULT_NS + ); + + let interceptedRequestBody: UsageRecord[] = []; + await retry.try(async () => { + interceptedRequestBody = getInterceptedRequestPayload(); + expect(interceptedRequestBody.length).to.greaterThan(0); + if (interceptedRequestBody.length > 0) { + const usageSubTypes = interceptedRequestBody.map((record) => record.usage.sub_type); + expect(usageSubTypes).to.contain('cspm'); + } + }); + + expect(interceptedRequestBody[0].usage.type).to.be('cloud_security'); + expect(interceptedRequestBody[0].usage.quantity).to.be(billableFindings.length); + }); + + it('Should intercept usage API request for KSPM', async () => { + await createPackagePolicy( + supertestWithoutAuth, + agentPolicyId, + 'kspm', + 'cloudbeat/cis_k8s', + 'vanilla', + 'kspm', + 'KSPM-1', + roleAuthc, + internalRequestHeader + ); + const billableFindings = getMockFindings({ + postureType: 'kspm', + isBillableAsset: true, + numberOfFindings: 3, + }); + + const notBillableFindings = getMockFindings({ + postureType: 'kspm', + isBillableAsset: false, + numberOfFindings: 11, + }); + + await addIndex( + es, + [...billableFindings, ...notBillableFindings], + LATEST_FINDINGS_INDEX_DEFAULT_NS + ); + + let interceptedRequestBody: UsageRecord[] = []; + + await retry.try(async () => { + interceptedRequestBody = getInterceptedRequestPayload(); + expect(interceptedRequestBody.length).to.greaterThan(0); + if (interceptedRequestBody.length > 0) { + const usageSubTypes = interceptedRequestBody.map((record) => record.usage.sub_type); + expect(usageSubTypes).to.contain('kspm'); + } + }); + + expect(interceptedRequestBody[0].usage.type).to.be('cloud_security'); + expect(interceptedRequestBody[0].usage.quantity).to.be(billableFindings.length); + }); + + it('Should intercept usage API request for CNVM', async () => { + await createPackagePolicy( + supertestWithoutAuth, + agentPolicyId, + 'vuln_mgmt', + 'cloudbeat/vuln_mgmt_aws', + 'aws', + 'vuln_mgmt', + 'CNVM-1', + roleAuthc, + internalRequestHeader + ); + + const billableFindings = getMockFindings({ + postureType: 'cnvm', + numberOfFindings: 2, + }); + + await addIndex(es, billableFindings, LATEST_VULNERABILITIES_INDEX_DEFAULT_NS); + + let interceptedRequestBody: UsageRecord[] = []; + + await retry.try(async () => { + interceptedRequestBody = getInterceptedRequestPayload(); + expect(interceptedRequestBody.length).to.greaterThan(0); + if (interceptedRequestBody.length > 0) { + const usageSubTypes = interceptedRequestBody.map((record) => record.usage.sub_type); + expect(usageSubTypes).to.contain('cnvm'); + } + }); + + expect(interceptedRequestBody[0].usage.type).to.be('cloud_security'); + expect(interceptedRequestBody[0].usage.quantity).to.be(billableFindings.length); + }); + + it('Should intercept usage API request for Defend for Containers', async () => { + await createCloudDefendPackagePolicy( + supertestWithoutAuth, + agentPolicyId, + roleAuthc, + internalRequestHeader + ); + + const blockActionEnabledHeartbeats = getMockDefendForContainersHeartbeats({ + isBlockActionEnables: true, + numberOfHearbeats: 2, + }); + + const blockActionDisabledHeartbeats = getMockDefendForContainersHeartbeats({ + isBlockActionEnables: false, + numberOfHearbeats: 2, + }); + await addIndex( + es, + [...blockActionEnabledHeartbeats, ...blockActionDisabledHeartbeats], + CLOUD_DEFEND_HEARTBEAT_INDEX_DEFAULT_NS + ); + + let interceptedRequestBody: UsageRecord[] = []; + + await retry.try(async () => { + interceptedRequestBody = getInterceptedRequestPayload(); + expect(interceptedRequestBody.length).to.greaterThan(0); + if (interceptedRequestBody.length > 0) { + const usageSubTypes = interceptedRequestBody.map((record) => record.usage.sub_type); + expect(usageSubTypes).to.contain('cloud_defend'); + } + }); + + expect(interceptedRequestBody.length).to.be(blockActionEnabledHeartbeats.length); + expect(interceptedRequestBody[0].usage.type).to.be('cloud_security'); + }); + + it('Should intercept usage API request with all integrations usage records', async () => { + // Create one package policy - it takes care forCSPM, KSMP and CNVM + await createPackagePolicy( + supertestWithoutAuth, + agentPolicyId, + 'cspm', + 'cloudbeat/cis_aws', + 'aws', + 'cspm', + 'CSPM-1', + roleAuthc, + internalRequestHeader + ); + + // Create Defend for Containers package policy + await createCloudDefendPackagePolicy( + supertestWithoutAuth, + agentPolicyId, + roleAuthc, + internalRequestHeader + ); + const billableFindingsCSPM = getMockFindings({ + postureType: 'cspm', + isBillableAsset: true, + numberOfFindings: 5, + }); + + const notBillableFindingsCSPM = getMockFindings({ + postureType: 'cspm', + isBillableAsset: false, + numberOfFindings: 10, + }); + + const billableFindingsKSPM = getMockFindings({ + postureType: 'kspm', + isBillableAsset: true, + numberOfFindings: 3, + }); + + const billableFindingsCNVM = getMockFindings({ + postureType: 'cnvm', + numberOfFindings: 2, + }); + + const notBillableFindingsKSPM = getMockFindings({ + postureType: 'kspm', + isBillableAsset: false, + numberOfFindings: 11, + }); + + const blockActionEnabledHeartbeats = getMockDefendForContainersHeartbeats({ + isBlockActionEnables: true, + numberOfHearbeats: 2, + }); + + const blockActionDisabledHeartbeats = getMockDefendForContainersHeartbeats({ + isBlockActionEnables: false, + numberOfHearbeats: 2, + }); + + await Promise.all([ + addIndex( + es, + [ + ...billableFindingsCSPM, + ...notBillableFindingsCSPM, + ...billableFindingsKSPM, + ...notBillableFindingsKSPM, + ], + LATEST_FINDINGS_INDEX_DEFAULT_NS + ), + addIndex(es, [...billableFindingsCNVM], LATEST_VULNERABILITIES_INDEX_DEFAULT_NS), + addIndex( + es, + [...blockActionEnabledHeartbeats, ...blockActionDisabledHeartbeats], + CLOUD_DEFEND_HEARTBEAT_INDEX_DEFAULT_NS + ), + ]); + + // Intercept and verify usage API request + let interceptedRequestBody: UsageRecord[] = []; + + await retry.try(async () => { + interceptedRequestBody = getInterceptedRequestPayload(); + const usageSubTypes = interceptedRequestBody.map((record) => record.usage.sub_type); + + expect(usageSubTypes).to.contain('cspm'); + expect(usageSubTypes).to.contain('kspm'); + expect(usageSubTypes).to.contain('cnvm'); + expect(usageSubTypes).to.contain('cloud_defend'); + }); + + const totalUsageQuantity = interceptedRequestBody.reduce( + (acc, record) => acc + record.usage.quantity, + 0 + ); + expect(totalUsageQuantity).to.be( + billableFindingsCSPM.length + + billableFindingsKSPM.length + + billableFindingsCNVM.length + + blockActionEnabledHeartbeats.length + ); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/mock_data.ts b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/mock_data.ts new file mode 100644 index 0000000000000..2455c8e1762cc --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/mock_data.ts @@ -0,0 +1,110 @@ +/* + * 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 Chance from 'chance'; + +const chance = new Chance(); + +// see https://github.com/elastic/security-team/issues/8970 for billable asset definition +export const BILLABLE_ASSETS_CONFIG = { + cspm: [ + // 'aws-ebs', we can't include EBS volumes until https://github.com/elastic/security-team/issues/9283 is resolved + // 'aws-ec2', we can't include EC2 instances until https://github.com/elastic/security-team/issues/9254 is resolved + 'aws-s3', + 'aws-rds', + 'azure-disk', + 'azure-document-db-database-account', + 'azure-flexible-mysql-server-db', + 'azure-flexible-postgresql-server-db', + 'azure-mysql-server-db', + 'azure-postgresql-server-db', + 'azure-sql-server', + 'azure-storage-account', + 'azure-vm', + 'gcp-bigquery-dataset', + 'gcp-compute-disk', + 'gcp-compute-instance', + 'gcp-sqladmin-instance', + 'gcp-storage-bucket', + ], + kspm: ['Node', 'node'], +}; +export const getMockFindings = ({ + postureType, + isBillableAsset, + numberOfFindings, +}: { + postureType: string; + isBillableAsset?: boolean; + numberOfFindings: number; +}) => { + return Array.from({ length: numberOfFindings }, () => mockFiniding(postureType, isBillableAsset)); +}; + +const mockFiniding = (postureType: string, isBillableAsset?: boolean) => { + if (postureType === 'cspm') { + const randomAsset = isBillableAsset + ? chance.pickone(BILLABLE_ASSETS_CONFIG.cspm) + : 'not-billable-asset'; + return { + resource: { id: chance.guid(), sub_type: randomAsset }, + rule: { + benchmark: { + posture_type: 'cspm', + }, + }, + }; + } + if (postureType === 'kspm') { + const randomAsset = isBillableAsset + ? chance.pickone(BILLABLE_ASSETS_CONFIG.kspm) + : 'not-billable-asset'; + + return { + resource: { id: chance.guid(), sub_type: randomAsset }, + rule: { + benchmark: { + posture_type: 'kspm', + }, + }, + agent: { id: chance.guid() }, + }; + } + if (postureType === 'cnvm') { + return { + cloud: { + instance: { + id: chance.guid(), + }, + }, + }; + } +}; + +export const getMockDefendForContainersHeartbeats = ({ + isBlockActionEnables, + numberOfHearbeats, +}: { + isBlockActionEnables: boolean; + numberOfHearbeats: number; +}) => { + return Array.from({ length: numberOfHearbeats }, () => + mockDefendForContainersHeartbeats(isBlockActionEnables) + ); +}; +const mockDefendForContainersHeartbeats = (isBlockActionEnables: boolean) => { + return { + agent: { + id: chance.guid(), + }, + cloud_defend: { + block_action_enabled: isBlockActionEnables, + }, + event: { + ingested: new Date().toISOString(), + }, + }; +}; diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/mock_usage_server.ts b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/mock_usage_server.ts new file mode 100644 index 0000000000000..1ed5e8d7ff09e --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/serverless_metering/mock_usage_server.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createServer } from '@mswjs/http-middleware'; + +import { http, HttpResponse, StrictResponse } from 'msw'; + +export const setupMockServer = () => { + const server = createServer(usageAPIHandler); + + return server; +}; +export interface UsageRecord { + id: string; + usage_timestamp: string; + creation_timestamp: string; + usage: UsageMetrics; + source: UsageSource; +} + +export interface UsageMetrics { + type: string; + sub_type?: string; + quantity: number; + period_seconds?: number; + cause?: string; + metadata?: unknown; +} + +export interface UsageSource { + id: string; + instance_group_id: string; + metadata?: UsageSourceMetadata; +} + +export interface UsageSourceMetadata { + tier?: string; +} + +interface UsageResponse { + response: UsageRecord[]; +} + +let interceptedRequestPayload: UsageRecord[] = []; + +const usageAPIHandler = http.post( + 'api/v1/usage', + async ({ request }): Promise> => { + const payload = (await request.clone().json()) as UsageRecord[]; + interceptedRequestPayload = payload; + + return HttpResponse.json({ + response: payload, + }); + } +); + +export const getInterceptedRequestPayload = (): UsageRecord[] => interceptedRequestPayload; diff --git a/x-pack/test_serverless/api_integration/test_suites/security/config.ts b/x-pack/test_serverless/api_integration/test_suites/security/config.ts index 4de07d59ea56a..b97ed38960561 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/config.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/config.ts @@ -23,5 +23,7 @@ export default createTestConfig({ `--xpack.task_manager.unsafe.exclude_task_types=${JSON.stringify(['Fleet-Metrics-Task'])}`, // useful for testing (also enabled in MKI QA) '--coreApp.allowDynamicConfigOverrides=true', + `--xpack.securitySolutionServerless.cloudSecurityUsageReportingTaskInterval=5s`, + `--xpack.securitySolutionServerless.usageReportingApiUrl=http://localhost:8081/api/v1/usage`, ], }); diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_homepage.ts b/x-pack/test_serverless/functional/page_objects/svl_search_homepage.ts index eeb1b6de731f9..44bdd417330a3 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_homepage.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_homepage.ts @@ -22,5 +22,47 @@ export function SvlSearchHomePageProvider({ getService }: FtrProviderContext) { async expectHomepageHeader() { await testSubjects.existOrFail('search-homepage-header', { timeout: 2000 }); }, + async expectConsoleLinkExists() { + await testSubjects.existOrFail('searchHomepageEmbeddedConsoleButton'); + }, + async clickConsoleLink() { + await testSubjects.click('searchHomepageEmbeddedConsoleButton'); + }, + async expectEndpointsLinkExists() { + await testSubjects.existOrFail('searchHomepageEndpointsHeaderActionEndpointsApiKeysButton'); + }, + async clickEndpointsLink() { + await testSubjects.click('searchHomepageEndpointsHeaderActionEndpointsApiKeysButton'); + }, + async expectConnectionDetailsFlyoutToBeOpen() { + await testSubjects.existOrFail('connectionDetailsModalTitle'); + }, + async closeConnectionDetailsFlyout() { + await testSubjects.existOrFail('euiFlyoutCloseButton'); + await testSubjects.click('euiFlyoutCloseButton'); + }, + async expectEndpointsTabIsAvailable() { + await testSubjects.existOrFail('connectionDetailsTabBtn-endpoints'); + await testSubjects.click('connectionDetailsTabBtn-endpoints'); + await testSubjects.existOrFail('connectionDetailsEsUrl'); + await testSubjects.existOrFail('connectionDetailsCloudIdSwitch'); + }, + async expectAPIKeyTabIsAvailable() { + await testSubjects.existOrFail('connectionDetailsTabBtn-apiKeys'); + await testSubjects.click('connectionDetailsTabBtn-apiKeys'); + await testSubjects.existOrFail('connectionDetailsApiKeyNameInput'); + await testSubjects.existOrFail('connectionDetailsApiKeySubmitBtn'); + }, + async createApiKeyInFlyout(keyName: string) { + await testSubjects.existOrFail('connectionDetailsApiKeyNameInput'); + await testSubjects.existOrFail('connectionDetailsApiKeySubmitBtn'); + await testSubjects.setValue('connectionDetailsApiKeyNameInput', keyName); + await testSubjects.click('connectionDetailsApiKeySubmitBtn'); + await testSubjects.existOrFail('connectionDetailsApiKeySuccessForm'); + await testSubjects.existOrFail('connectionDetailsApiKeyValueRow'); + expect(await testSubjects.getVisibleText('connectionDetailsApiKeySuccessForm')).contain( + keyName + ); + }, }; } diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/_data_source_profile.ts b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/_data_source_profile.ts index 317835958b0a9..9ce2e79d8f586 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/_data_source_profile.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/_data_source_profile.ts @@ -13,6 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker', 'discover', 'unifiedFieldList']); const testSubjects = getService('testSubjects'); const dataViews = getService('dataViews'); + const dataGrid = getService('dataGrid'); describe('data source profile', () => { describe('ES|QL mode', () => { @@ -53,6 +54,40 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await logLevels[2].getVisibleText()).to.be('Info'); }); }); + + describe('doc viewer extension', () => { + it('should not render custom doc viewer view', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-* | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.missingOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be('Result'); + }); + + it('should render custom doc viewer view', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-logs | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.existOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be('Record #0'); + }); + }); }); describe('data view mode', () => { @@ -83,6 +118,32 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await logLevels[2].getVisibleText()).to.be('Info'); }); }); + + describe('doc viewer extension', () => { + it('should not render custom doc viewer view', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-*'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.missingOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be('Document'); + }); + + it('should render custom doc viewer view', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-logs'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataGrid.clickRowToggle({ rowIndex: 0 }); + await testSubjects.existOrFail('docViewerTab-doc_view_table'); + await testSubjects.existOrFail('docViewerTab-doc_view_source'); + await testSubjects.existOrFail('docViewerTab-doc_view_logs_overview'); + expect(await testSubjects.getVisibleText('docViewerRowDetailsTitle')).to.be( + 'Record #my-example-logs::XdQFDpABfGznVC1bCHLo::' + ); + }); + }); }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts b/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts index 8e9b33ffa1beb..5c8fd77347abd 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts @@ -176,6 +176,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); + it('should be able to search with fuzzy search (1 typo)', async function () { + await PageObjects.unifiedFieldList.findFieldByName('rel4tedContent.art'); + + await retry.waitFor('updates', async () => { + return ( + (await PageObjects.unifiedFieldList.getSidebarAriaDescription()) === + '4 available fields. 0 meta fields.' + ); + }); + + expect( + (await PageObjects.unifiedFieldList.getSidebarSectionFieldNames('available')).join(', ') + ).to.be( + 'relatedContent.article:modified_time, relatedContent.article:published_time, relatedContent.article:section, relatedContent.article:tag' + ); + }); + it('should ignore empty search', async function () { await PageObjects.unifiedFieldList.findFieldByName(' '); // only spaces diff --git a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts index 7de972a7b2c88..2751abf569746 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_flyout.ts @@ -54,10 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const degradedDatasetName = datasetNames[2]; - // Failing: See https://github.com/elastic/kibana/issues/187566 - describe.skip('Flyout', function () { - // see details: https://github.com/elastic/kibana/issues/187624 - this.tags(['failsOnMKI']); + describe('Flyout', function () { before(async () => { // Install Apache Integration and ingest logs for it await PageObjects.observabilityLogsExplorer.installPackage(apachePkg); @@ -158,17 +155,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should shows the integration section for integrations', async () => { await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName); - const integrationNameElements = await PageObjects.datasetQuality.getFlyoutElementsByText( - '[data-test-subj=datasetQualityFlyoutFieldValue]', - apacheIntegrationId - ); - await testSubjects.existOrFail( PageObjects.datasetQuality.testSubjectSelectors .datasetQualityFlyoutFieldsListIntegrationDetails ); - expect(integrationNameElements.length).to.eql(1); + await retry.tryForTime(5000, async () => { + const integrationNameExists = await PageObjects.datasetQuality.doesTextExist( + PageObjects.datasetQuality.testSubjectSelectors + .datasetQualityFlyoutFieldsListIntegrationDetails, + apacheIntegrationId + ); + expect(integrationNameExists).to.be(true); + }); await PageObjects.datasetQuality.closeFlyout(); }); diff --git a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/home.ts b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/home.ts index 7deeeff7e9bc1..a1c285146e5e5 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/home.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/home.ts @@ -49,6 +49,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('dataset quality table exists', async () => { await PageObjects.datasetQuality.navigateTo(); + await PageObjects.datasetQuality.waitUntilTableLoaded(); await testSubjects.existOrFail( PageObjects.datasetQuality.testSubjectSelectors.datasetQualityTable ); diff --git a/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts index f05b5975c90cb..c4b29fdfb443d 100644 --- a/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts @@ -19,9 +19,11 @@ export default createTestConfig({ suiteTags: { exclude: ['skipSvlSearch'] }, // add feature flags kbnServerArgs: [ - `--xpack.security.roleManagementEnabled=true`, + `--xpack.cloud.id='ES3_FTR_TESTS:ZmFrZS1kb21haW4uY2xkLmVsc3RjLmNvJGZha2Vwcm9qZWN0aWQuZXMkZmFrZXByb2plY3RpZC5rYg=='`, + `--xpack.cloud.serverless.project_id='fakeprojectid'`, `--xpack.cloud.base_url='https://cloud.elastic.co'`, `--xpack.cloud.organization_url='/account/members'`, + `--xpack.security.roleManagementEnabled=true`, ], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], diff --git a/x-pack/test_serverless/functional/test_suites/search/config.ts b/x-pack/test_serverless/functional/test_suites/search/config.ts index c2171c532bf23..6853e75d987b8 100644 --- a/x-pack/test_serverless/functional/test_suites/search/config.ts +++ b/x-pack/test_serverless/functional/test_suites/search/config.ts @@ -18,4 +18,8 @@ export default createTestConfig({ // include settings from project controller // https://github.com/elastic/project-controller/blob/main/internal/project/esproject/config/elasticsearch.yml esServerArgs: [], + kbnServerArgs: [ + `--xpack.cloud.id='ES3_FTR_TESTS:ZmFrZS1kb21haW4uY2xkLmVsc3RjLmNvJGZha2Vwcm9qZWN0aWQuZXMkZmFrZXByb2plY3RpZC5rYg=='`, + `--xpack.cloud.serverless.project_id='fakeprojectid'`, + ], }); diff --git a/x-pack/test_serverless/functional/test_suites/search/search_homepage.ts b/x-pack/test_serverless/functional/test_suites/search/search_homepage.ts index 1f627921d1592..93af4de797cfa 100644 --- a/x-pack/test_serverless/functional/test_suites/search/search_homepage.ts +++ b/x-pack/test_serverless/functional/test_suites/search/search_homepage.ts @@ -36,22 +36,48 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('has search homepage with Home sidenav', async () => { - pageObjects.svlSearchHomePage.expectToBeOnHomepage(); - pageObjects.svlSearchHomePage.expectHomepageHeader(); + await pageObjects.svlSearchHomePage.expectToBeOnHomepage(); + await pageObjects.svlSearchHomePage.expectHomepageHeader(); // Navigate to another page await pageObjects.svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'serverlessConnectors', }); - pageObjects.svlSearchHomePage.expectToNotBeOnHomepage(); + await pageObjects.svlSearchHomePage.expectToNotBeOnHomepage(); // Click Home in Side nav await pageObjects.svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'searchHomepage', }); - pageObjects.svlSearchHomePage.expectToBeOnHomepage(); + await pageObjects.svlSearchHomePage.expectToBeOnHomepage(); }); it('has embedded dev console', async () => { await testHasEmbeddedConsole(pageObjects); }); + + it('has console quickstart link on page', async () => { + await pageObjects.svlSearchHomePage.expectConsoleLinkExists(); + await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed(); + await pageObjects.svlSearchHomePage.clickConsoleLink(); + await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeOpen(); + await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleControlBar(); + await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed(); + }); + + it('has endpoints link and flyout', async () => { + await pageObjects.svlSearchHomePage.expectEndpointsLinkExists(); + await pageObjects.svlSearchHomePage.clickEndpointsLink(); + await pageObjects.svlSearchHomePage.expectConnectionDetailsFlyoutToBeOpen(); + await pageObjects.svlSearchHomePage.expectEndpointsTabIsAvailable(); + await pageObjects.svlSearchHomePage.closeConnectionDetailsFlyout(); + }); + + it('can create an API key', async () => { + await pageObjects.svlSearchHomePage.expectEndpointsLinkExists(); + await pageObjects.svlSearchHomePage.clickEndpointsLink(); + await pageObjects.svlSearchHomePage.expectConnectionDetailsFlyoutToBeOpen(); + await pageObjects.svlSearchHomePage.expectAPIKeyTabIsAvailable(); + await pageObjects.svlSearchHomePage.createApiKeyInFlyout('ftr-test-key'); + await pageObjects.svlSearchHomePage.closeConnectionDetailsFlyout(); + }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts b/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts index 33e2e9883b38b..b600da8ae4343 100644 --- a/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts +++ b/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts @@ -12,7 +12,6 @@ import { RoleCredentials } from '../../../../shared/services'; import { createOpenAIConnector } from './utils/create_openai_connector'; import { createLlmProxy, LlmProxy } from './utils/create_llm_proxy'; -const indexName = 'basic_index'; const esArchiveIndex = 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'; export default function ({ getPageObjects, getService }: FtrProviderContext) { @@ -66,6 +65,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToExist(); await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToDisabled(); await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageIndexCalloutExists(); }); describe('with gen ai connectors', () => { @@ -78,8 +78,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await removeOpenAIConnector?.(); await browser.refresh(); }); - it('hide gen ai panel', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnector(); + it('show success llm button', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectShowSuccessLLMButton(); }); }); @@ -90,7 +90,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('creates a connector successfully', async () => { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenConnectorPagePlayground(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnectorAfterCreatingConnector( + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSuccessButtonAfterCreatingConnector( createConnector ); }); @@ -102,17 +102,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('without any indices', () => { - it('show no index callout', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectNoIndexCalloutExists(); - }); - it('hide no index callout when index added', async () => { await createIndex(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenFlyoutAndSelectIndex(); }); after(async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.removeIndexFromComboBox(); await esArchiver.unload(esArchiveIndex); await browser.refresh(); }); @@ -125,14 +120,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await browser.refresh(); }); - it('dropdown shows up', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectIndicesInDropdown(); - }); - - it('can select index from dropdown and navigate to chat window', async () => { - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToSelectIndicesAndStartButtonEnabled( - indexName - ); + it('can select index from dropdown and load chat page', async () => { + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToSelectIndicesAndLoadChat(); }); after(async () => { @@ -148,7 +137,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await createConnector(); await createIndex(); await browser.refresh(); - await pageObjects.searchPlayground.PlaygroundChatPage.navigateToChatPage(indexName); + await pageObjects.searchPlayground.PlaygroundChatPage.navigateToChatPage(); }); it('loads successfully', async () => { await pageObjects.searchPlayground.PlaygroundChatPage.expectChatWindowLoaded(); diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index 2645774f7c9ab..d5fb5a12f9b1d 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -104,5 +104,6 @@ "@kbn/config-schema", "@kbn/features-plugin", "@kbn/observability-ai-assistant-plugin", + "@kbn/ml-trained-models-utils", ] } diff --git a/yarn.lock b/yarn.lock index 67e950497963b..f74f5ec3fc6f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4853,6 +4853,10 @@ version "0.0.0" uid "" +"@kbn/esql@link:src/plugins/esql": + version "0.0.0" + uid "" + "@kbn/event-annotation-common@link:packages/kbn-event-annotation-common": version "0.0.0" uid "" @@ -5833,6 +5837,10 @@ version "0.0.0" uid "" +"@kbn/recently-accessed@link:packages/kbn-recently-accessed": + version "0.0.0" + uid "" + "@kbn/remote-clusters-plugin@link:x-pack/plugins/remote_clusters": version "0.0.0" uid "" @@ -6661,10 +6669,6 @@ version "0.0.0" uid "" -"@kbn/text-based-languages@link:src/plugins/text_based_languages": - version "0.0.0" - uid "" - "@kbn/third-party-lens-navigation-prompt-plugin@link:x-pack/examples/third_party_lens_navigation_prompt": version "0.0.0" uid "" @@ -7402,6 +7406,14 @@ resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-1.1.0.tgz#1528eb43630caf83a1d75d5332b30e75e9bb1b5b" integrity sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw== +"@mswjs/http-middleware@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@mswjs/http-middleware/-/http-middleware-0.10.1.tgz#7ff38c5398ba01efe98e16a0da24d612ffd69ccc" + integrity sha512-I58RDHXzwKjMjaSO7aV7X0zUzbAvUEEDYsL9+I6Ro0zF5LgjnNdpZoMufG/WoYEenhDCgv7K+HMpLxGuhlfjKA== + dependencies: + express "^4.18.2" + strict-event-emitter "^0.5.1" + "@mswjs/interceptors@^0.29.0": version "0.29.1" resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.29.1.tgz#e77fc58b5188569041d0440b25c9e9ebb1ccd60a" @@ -10375,11 +10387,6 @@ resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f" integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== -"@types/js-levenshtein@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.0.tgz#9541eec4ad6e3ec5633270a3a2b55d981edc44a9" - integrity sha512-14t0v1ICYRtRVcHASzes0v/O+TIeASb8aD55cWF1PidtInhFWSXcmhzhHqGjUWf9SUq1w70cvd1cWKUULubAfQ== - "@types/js-search@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@types/js-search/-/js-search-1.4.0.tgz#f2d4afa176a4fc7b17fb46a1593847887fa1fb7b" @@ -17508,7 +17515,7 @@ expr-eval@^2.0.2: resolved "https://registry.yarnpkg.com/expr-eval/-/expr-eval-2.0.2.tgz#fa6f044a7b0c93fde830954eb9c5b0f7fbc7e201" integrity sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg== -express@^4.17.1, express@^4.17.3: +express@^4.17.1, express@^4.17.3, express@^4.18.2: version "4.19.2" resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==