diff --git a/.buildkite/ftr_oblt_serverless_configs.yml b/.buildkite/ftr_oblt_serverless_configs.yml index 9534e62926f06..085c25f2d80a6 100644 --- a/.buildkite/ftr_oblt_serverless_configs.yml +++ b/.buildkite/ftr_oblt_serverless_configs.yml @@ -1,5 +1,6 @@ disabled: # Base config files, only necessary to inform config finding script + - x-pack/test_serverless/functional/test_suites/observability/cypress/oblt_config.base.ts # Cypress configs, for now these are still run manually - x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts diff --git a/.buildkite/ftr_oblt_stateful_configs.yml b/.buildkite/ftr_oblt_stateful_configs.yml index 7cecfd5e8d665..4edf75f385816 100644 --- a/.buildkite/ftr_oblt_stateful_configs.yml +++ b/.buildkite/ftr_oblt_stateful_configs.yml @@ -10,6 +10,9 @@ disabled: - x-pack/plugins/observability_solution/profiling/e2e/ftr_config_runner.ts - x-pack/plugins/observability_solution/profiling/e2e/ftr_config.ts + #FTR configs + - x-pack/plugins/observability_solution/uptime/e2e/config.ts + # Elastic Synthetics configs - x-pack/plugins/observability_solution/uptime/e2e/uptime/synthetics_run.ts - x-pack/plugins/observability_solution/synthetics/e2e/config.ts @@ -27,6 +30,10 @@ enabled: - x-pack/test/api_integration/apis/synthetics/config.ts - x-pack/test/api_integration/apis/slos/config.ts - x-pack/test/api_integration/apis/uptime/config.ts + - x-pack/test/apm_api_integration/basic/config.ts + - x-pack/test/apm_api_integration/cloud/config.ts + - x-pack/test/apm_api_integration/rules/config.ts + - x-pack/test/apm_api_integration/trial/config.ts - x-pack/test/dataset_quality_api_integration/basic/config.ts - x-pack/test/functional/apps/observability_logs_explorer/config.ts - x-pack/test/functional/apps/dataset_quality/config.ts diff --git a/.buildkite/ftr_platform_stateful_configs.yml b/.buildkite/ftr_platform_stateful_configs.yml index a0425f766f569..96c15cce513c6 100644 --- a/.buildkite/ftr_platform_stateful_configs.yml +++ b/.buildkite/ftr_platform_stateful_configs.yml @@ -8,6 +8,8 @@ disabled: - x-pack/test/functional_with_es_ssl/config.base.ts - x-pack/test/api_integration/config.ts - x-pack/test/fleet_api_integration/config.base.ts + - x-pack/test/functional_basic/apps/ml/config.base.ts + - x-pack/test/functional_basic/apps/transform/config.base.ts # QA suites that are run out-of-band - x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js diff --git a/.buildkite/ftr_security_serverless_configs.yml b/.buildkite/ftr_security_serverless_configs.yml index 51e3eba941c6b..3880175623fdd 100644 --- a/.buildkite/ftr_security_serverless_configs.yml +++ b/.buildkite/ftr_security_serverless_configs.yml @@ -1,5 +1,7 @@ disabled: # Base config files, only necessary to inform config finding script + - x-pack/test_serverless/functional/test_suites/security/cypress/security_config.base.ts + - x-pack/test_serverless/functional/test_suites/security/cypress/cypress.config.ts - x-pack/test/security_solution_api_integration/config/serverless/config.base.ts - x-pack/test/security_solution_api_integration/config/serverless/config.base.essentials.ts - x-pack/test/security_solution_api_integration/config/serverless/config.base.edr_workflows.ts diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index 8f1605b363e3d..a7931bab0a68d 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -5,6 +5,7 @@ disabled: - x-pack/test/security_solution_api_integration/config/ess/config.base.edr_workflows.trial.ts - x-pack/test/security_solution_api_integration/config/ess/config.base.edr_workflows.ts - x-pack/test/security_solution_api_integration/config/ess/config.base.basic.ts + - x-pack/test/security_solution_api_integration/config/ess/config.base.trial.ts - x-pack/test/security_solution_endpoint/configs/config.base.ts - x-pack/test/security_solution_endpoint/config.base.ts - x-pack/test/security_solution_endpoint_api_int/config.base.ts diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 8b57562c7a329..e5da8ce788e5b 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -14,7 +14,7 @@ steps: preemptible: true key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" - timeout_in_minutes: 60 + timeout_in_minutes: 90 retry: automatic: - exit_status: '-1' diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index cd5d9aa470b3c..db6be6c6e83f7 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { execSync } from 'child_process'; import fs from 'fs'; import prConfigs from '../../../pull_requests.json'; import { areChangesSkippable, doAnyChangesMatch, getAgentImageConfig } from '#pipeline-utils'; const prConfig = prConfigs.jobs.find((job) => job.pipelineSlug === 'kibana-pull-request'); +const emptyStep = `steps: []`; if (!prConfig) { console.error(`'kibana-pull-request' pipeline not found in .buildkite/pull_requests.json`); @@ -28,21 +28,16 @@ const getPipeline = (filename: string, removeSteps = true) => { }; (async () => { + const pipeline: string[] = []; + try { const skippable = await areChangesSkippable(SKIPPABLE_PR_MATCHERS, REQUIRED_PATHS); if (skippable) { - console.log('All changes in PR are skippable. Skipping CI.'); - - // Since we skip everything, including post-build, we need to at least make sure the commit status gets set - execSync('BUILD_SUCCESSFUL=true .buildkite/scripts/lifecycle/commit_status_complete.sh', { - stdio: 'inherit', - }); - process.exit(0); + console.log(emptyStep); + return; } - const pipeline = []; - pipeline.push(getAgentImageConfig({ returnYaml: true })); pipeline.push(getPipeline('.buildkite/pipelines/pull_request/base.yml', false)); diff --git a/docs/management/connectors/action-types/gemini.asciidoc b/docs/management/connectors/action-types/gemini.asciidoc index 3c835d981465c..610fb4ad48f15 100644 --- a/docs/management/connectors/action-types/gemini.asciidoc +++ b/docs/management/connectors/action-types/gemini.asciidoc @@ -56,7 +56,7 @@ Body:: A stringified JSON payload sent to the {gemini} invoke model API. Fo body: JSON.stringify({ contents: [{ role: user, - parts: [{ text: 'Write the first line of a story about a magic backpack.' }] + parts: [{ text: 'Hello world!' }] }], generation_config: { temperature: 0, diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index 5ccacb4c8acfb..32cb7b7436e13 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -395,7 +395,8 @@ "description": "Kibana's operational status. A minimal response is sent for unauthorized users." } } - } + }, + "description": "Overall status is OK and Kibana should be functioning normally." }, "503": { "content": { @@ -412,7 +413,8 @@ "description": "Kibana's operational status. A minimal response is sent for unauthorized users." } } - } + }, + "description": "Kibana or some of it's essential services are unavailable. Kibana may be degraded or unavailable." } }, "summary": "Get Kibana's current status", diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index 5ccacb4c8acfb..32cb7b7436e13 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -395,7 +395,8 @@ "description": "Kibana's operational status. A minimal response is sent for unauthorized users." } } - } + }, + "description": "Overall status is OK and Kibana should be functioning normally." }, "503": { "content": { @@ -412,7 +413,8 @@ "description": "Kibana's operational status. A minimal response is sent for unauthorized users." } } - } + }, + "description": "Kibana or some of it's essential services are unavailable. Kibana may be degraded or unavailable." } }, "summary": "Get Kibana's current status", diff --git a/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.mocks.ts b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.mocks.ts deleted file mode 100644 index 0d0c637a768df..0000000000000 --- a/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.mocks.ts +++ /dev/null @@ -1,13 +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 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 const getApmConfigMock = jest.fn(); - -jest.doMock('./get_apm_config', () => ({ - getApmConfig: getApmConfigMock, -})); diff --git a/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.ts b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.ts index 481b0b694747a..acf8950d0d147 100644 --- a/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.ts +++ b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.test.ts @@ -6,10 +6,7 @@ * Side Public License, v 1. */ -import { getApmConfigMock } from './http_resources_service.test.mocks'; - import type { RouteConfig } from '@kbn/core-http-server'; - import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { httpServiceMock, httpServerMock } from '@kbn/core-http-server-mocks'; import { renderingServiceMock } from '@kbn/core-rendering-server-mocks'; @@ -29,11 +26,6 @@ describe('HttpResources service', () => { let router: ReturnType; const kibanaRequest = httpServerMock.createKibanaRequest(); const context = createCoreRequestHandlerContextMock(); - const apmConfig = { mockApmConfig: true }; - - beforeEach(() => { - getApmConfigMock.mockReturnValue(apmConfig); - }); describe('#createRegistrar', () => { beforeEach(() => { @@ -93,9 +85,6 @@ describe('HttpResources service', () => { }, { isAnonymousPage: false, - vars: { - apmConfig, - }, } ); }); @@ -118,9 +107,6 @@ describe('HttpResources service', () => { }, { isAnonymousPage: true, - vars: { - apmConfig, - }, } ); }); diff --git a/packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts index a896d6b98542f..bb9320bc5cb48 100644 --- a/packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts +++ b/packages/core/http/core-http-resources-server-internal/src/http_resources_service.ts @@ -33,8 +33,6 @@ import type { import type { InternalHttpResourcesSetup } from './types'; -import { getApmConfig } from './get_apm_config'; - /** * @internal */ @@ -112,13 +110,9 @@ export class HttpResourcesService implements CoreService { isConfigSchema( ( validationSchemas as () => RouteValidatorRequestAndResponses - )().response![200].body() + )().response![200].body!() ) ).toBe(true); expect( isConfigSchema( ( validationSchemas as () => RouteValidatorRequestAndResponses - )().response![404].body() + )().response![404].body!() ) ).toBe(true); } diff --git a/packages/core/http/core-http-router-server-internal/src/util.test.ts b/packages/core/http/core-http-router-server-internal/src/util.test.ts index 8febff7b113f6..0f615d7b58603 100644 --- a/packages/core/http/core-http-router-server-internal/src/util.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/util.test.ts @@ -21,6 +21,10 @@ describe('prepareResponseValidation', () => { 404: { body: jest.fn(() => schema.string()), }, + 500: { + description: 'just a description', + body: undefined, + }, unsafe: { body: true, }, @@ -32,13 +36,15 @@ describe('prepareResponseValidation', () => { expect(prepared).toEqual({ 200: { body: expect.any(Function) }, 404: { body: expect.any(Function) }, + 500: { description: 'just a description', body: undefined }, unsafe: { body: true }, }); - [1, 2, 3].forEach(() => prepared[200].body()); - [1, 2, 3].forEach(() => prepared[404].body()); + [1, 2, 3].forEach(() => prepared[200].body!()); + [1, 2, 3].forEach(() => prepared[404].body!()); expect(validation.response![200].body).toHaveBeenCalledTimes(1); expect(validation.response![404].body).toHaveBeenCalledTimes(1); + expect(validation.response![500].body).toBeUndefined(); }); }); diff --git a/packages/core/http/core-http-router-server-internal/src/util.ts b/packages/core/http/core-http-router-server-internal/src/util.ts index 75c18854f842f..5f854e2ee1568 100644 --- a/packages/core/http/core-http-router-server-internal/src/util.ts +++ b/packages/core/http/core-http-router-server-internal/src/util.ts @@ -9,25 +9,22 @@ import { once } from 'lodash'; import { isFullValidatorContainer, + type RouteValidatorFullConfigResponse, type RouteConfig, type RouteMethod, type RouteValidator, } from '@kbn/core-http-server'; -import type { ObjectType, Type } from '@kbn/config-schema'; -import type { ZodEsque } from '@kbn/zod'; function isStatusCode(key: string) { return !isNaN(parseInt(key, 10)); } -interface ResponseValidation { - [statusCode: number]: { body: () => ObjectType | Type | ZodEsque }; -} - -export function prepareResponseValidation(validation: ResponseValidation): ResponseValidation { +export function prepareResponseValidation( + validation: RouteValidatorFullConfigResponse +): RouteValidatorFullConfigResponse { const responses = Object.entries(validation).map(([key, value]) => { if (isStatusCode(key)) { - return [key, { body: once(value.body) }]; + return [key, { ...value, ...(value.body ? { body: once(value.body) } : {}) }]; } return [key, value]; }); diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts index 269d53cfd897c..db6efd97c6f6b 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts @@ -241,12 +241,12 @@ describe('Versioned route', () => { ] = route.handlers; const res200 = (validate as () => VersionedRouteValidation)() - .response![200].body; + .response![200].body!; expect(isConfigSchema(unwrapVersionedResponseBodyValidation(res200))).toBe(true); const res404 = (validate as () => VersionedRouteValidation)() - .response![404].body; + .response![404].body!; expect(isConfigSchema(unwrapVersionedResponseBodyValidation(res404))).toBe(true); @@ -301,6 +301,33 @@ describe('Versioned route', () => { expect(validateOutputFn).toHaveBeenCalledTimes(1); }); + it('handles "undefined" response schemas', async () => { + let handler: RequestHandler; + + (router.post as jest.Mock).mockImplementation((opts: unknown, fn) => (handler = fn)); + const versionedRouter = CoreVersionedRouter.from({ router, isDev: true }); + versionedRouter.post({ path: '/test/{id}', access: 'internal' }).addVersion( + { + version: '1', + validate: { response: { 500: { description: 'jest description', body: undefined } } }, + }, + async (ctx, req, res) => res.custom({ statusCode: 500 }) + ); + + await expect( + handler!( + {} as any, + createRequest({ + version: '1', + body: { foo: 1 }, + params: { foo: 1 }, + query: { foo: 1 }, + }), + responseFactory + ) + ).resolves.not.toThrow(); + }); + it('runs custom response validations', async () => { let handler: RequestHandler; const { fooValidation, validateBodyFn, validateOutputFn, validateParamsFn, validateQueryFn } = diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts index b6192dbe68a25..ea69a25e4b56a 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts @@ -191,13 +191,13 @@ export class CoreVersionedRoute implements VersionedRoute { const response = await handler.fn(ctx, req, res); - if (this.router.isDev && validation?.response?.[response.status]) { + if (this.router.isDev && validation?.response?.[response.status]?.body) { const { [response.status]: responseValidation, unsafe } = validation.response; try { validate( { body: response.payload }, { - body: unwrapVersionedResponseBodyValidation(responseValidation.body), + body: unwrapVersionedResponseBodyValidation(responseValidation.body!), unsafe: { body: unsafe?.body }, } ); diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/util.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/util.test.ts index b9ef74c11fa3c..ae9409fcffb9b 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/util.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/util.test.ts @@ -7,8 +7,12 @@ */ import { schema } from '@kbn/config-schema'; -import { VersionedRouteResponseValidation } from '@kbn/core-http-server'; -import { isCustomValidation, unwrapVersionedResponseBodyValidation } from './util'; +import type { VersionedRouteResponseValidation } from '@kbn/core-http-server'; +import { + isCustomValidation, + unwrapVersionedResponseBodyValidation, + prepareVersionedRouteValidation, +} from './util'; test.each([ [() => schema.object({}), false], @@ -17,6 +21,43 @@ test.each([ expect(isCustomValidation(input)).toBe(result); }); +describe('prepareVersionedRouteValidation', () => { + it('wraps only expected values', () => { + const validate = { + request: {}, + response: { + 200: { + body: jest.fn(() => schema.string()), + }, + 404: { + body: jest.fn(() => schema.string()), + }, + 500: { + description: 'just a description', + body: undefined, + }, + }, + }; + + const prepared = prepareVersionedRouteValidation({ + version: '1', + validate, + }); + + expect(prepared).toEqual({ + version: '1', + validate: { + request: {}, + response: { + 200: { body: expect.any(Function) }, + 404: { body: expect.any(Function) }, + 500: { description: 'just a description', body: undefined }, + }, + }, + }); + }); +}); + test('unwrapVersionedResponseBodyValidation', () => { const mySchema = schema.object({}); const custom = () => ({ value: 'ok' }); @@ -29,6 +70,6 @@ test('unwrapVersionedResponseBodyValidation', () => { }, }; - expect(unwrapVersionedResponseBodyValidation(validation[200].body)).toBe(mySchema); - expect(unwrapVersionedResponseBodyValidation(validation[404].body)).toBe(custom); + expect(unwrapVersionedResponseBodyValidation(validation[200].body!)).toBe(mySchema); + expect(unwrapVersionedResponseBodyValidation(validation[404].body!)).toBe(custom); }); diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/util.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/util.ts index ddd546be5bab5..88dad4eb50558 100644 --- a/packages/core/http/core-http-router-server-internal/src/versioned_router/util.ts +++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/util.ts @@ -30,7 +30,7 @@ export function isCustomValidation( * @internal */ export function unwrapVersionedResponseBodyValidation( - validation: VersionedRouteResponseValidation[number]['body'] + validation: VersionedResponseBodyValidation ): RouteValidationSpec { if (isCustomValidation(validation)) { return validation.custom; @@ -43,8 +43,15 @@ function prepareValidation(validation: VersionedRouteValidation = RouteValidatorConfig { }).toThrowError(); }); }); - -describe('setup.getInjectedVar()', () => { - it('returns values from injectedMetadata.vars', () => { - const setup = new InjectedMetadataService({ - injectedMetadata: { - vars: { - foo: { - bar: '1', - }, - 'baz:box': { - foo: 2, - }, - }, - }, - } as any).setup(); - - expect(setup.getInjectedVar('foo')).toEqual({ - bar: '1', - }); - expect(setup.getInjectedVar('foo.bar')).toBe('1'); - expect(setup.getInjectedVar('baz:box')).toEqual({ - foo: 2, - }); - expect(setup.getInjectedVar('')).toBe(undefined); - }); - - it('returns read-only values', () => { - const setup = new InjectedMetadataService({ - injectedMetadata: { - vars: { - foo: { - bar: 1, - }, - }, - }, - } as any).setup(); - - const foo: any = setup.getInjectedVar('foo'); - expect(() => { - foo.bar = 2; - }).toThrowErrorMatchingInlineSnapshot( - `"Cannot assign to read only property 'bar' of object '#'"` - ); - expect(() => { - foo.newProp = 2; - }).toThrowErrorMatchingInlineSnapshot( - `"Cannot add property newProp, object is not extensible"` - ); - }); -}); - -describe('setup.getInjectedVars()', () => { - it('returns all injected vars, readonly', () => { - const setup = new InjectedMetadataService({ - injectedMetadata: { - vars: { - foo: { - bar: 1, - }, - }, - }, - } as any).setup(); - - const vars: any = setup.getInjectedVars(); - expect(() => { - vars.foo = 2; - }).toThrowErrorMatchingInlineSnapshot( - `"Cannot assign to read only property 'foo' of object '#'"` - ); - expect(() => { - vars.newProp = 2; - }).toThrowErrorMatchingInlineSnapshot( - `"Cannot add property newProp, object is not extensible"` - ); - }); -}); diff --git a/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/injected_metadata_service.ts b/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/injected_metadata_service.ts index a50de7ca3f5e7..104500ef19215 100644 --- a/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/injected_metadata_service.ts +++ b/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/injected_metadata_service.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { get } from 'lodash'; import { deepFreeze } from '@kbn/std'; import type { InjectedMetadata } from '@kbn/core-injected-metadata-common-internal'; import type { @@ -76,14 +75,6 @@ export class InjectedMetadataService { return this.state.legacyMetadata; }, - getInjectedVar: (name: string, defaultValue?: any): unknown => { - return get(this.state.vars, name, defaultValue); - }, - - getInjectedVars: () => { - return this.state.vars; - }, - getKibanaBuildNumber: () => { return this.state.buildNumber; }, diff --git a/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/types.ts b/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/types.ts index bf77a2531660a..12bee868702b6 100644 --- a/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/types.ts +++ b/packages/core/injected-metadata/core-injected-metadata-browser-internal/src/types.ts @@ -56,10 +56,6 @@ export interface InternalInjectedMetadataSetup { user?: Record | undefined; }; }; - getInjectedVar: (name: string, defaultValue?: any) => unknown; - getInjectedVars: () => { - [key: string]: unknown; - }; getCustomBranding: () => CustomBranding; } diff --git a/packages/core/injected-metadata/core-injected-metadata-browser-mocks/src/injected_metadata_service.mock.ts b/packages/core/injected-metadata/core-injected-metadata-browser-mocks/src/injected_metadata_service.mock.ts index 69148ca90e31b..e2dad19650a2c 100644 --- a/packages/core/injected-metadata/core-injected-metadata-browser-mocks/src/injected_metadata_service.mock.ts +++ b/packages/core/injected-metadata/core-injected-metadata-browser-mocks/src/injected_metadata_service.mock.ts @@ -27,8 +27,6 @@ const createSetupContractMock = () => { getLegacyMetadata: jest.fn(), getTheme: jest.fn(), getPlugins: jest.fn(), - getInjectedVar: jest.fn(), - getInjectedVars: jest.fn(), getKibanaBuildNumber: jest.fn(), getCustomBranding: jest.fn(), }; diff --git a/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts b/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts index 6d4e3df08a5ef..c2f1e85e1e60d 100644 --- a/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts +++ b/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts @@ -71,7 +71,7 @@ export interface InjectedMetadata { warnLegacyBrowsers: boolean; }; externalUrl: { policy: InjectedMetadataExternalUrlPolicy[] }; - vars: Record; + apmConfig: Record | null; uiPlugins: InjectedMetadataPlugin[]; legacyMetadata: { uiSettings: { diff --git a/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap b/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap index 69f534cf837b4..e92e760b400e5 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap +++ b/packages/core/rendering/core-rendering-server-internal/src/__snapshots__/rendering_service.test.ts.snap @@ -3,6 +3,9 @@ exports[`RenderingService preboot() render() renders "core" CDN url injected 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "http://foo.bar:1773", "basePath": "/mock-server-basepath", "branch": Any, @@ -75,7 +78,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -83,6 +85,9 @@ Object { exports[`RenderingService preboot() render() renders "core" page 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -151,7 +156,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -159,6 +163,9 @@ Object { exports[`RenderingService preboot() render() renders "core" page driven by settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -231,7 +238,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -239,6 +245,9 @@ Object { exports[`RenderingService preboot() render() renders "core" page for blank basepath 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "", "branch": Any, @@ -307,7 +316,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -315,6 +323,9 @@ Object { exports[`RenderingService preboot() render() renders "core" page for unauthenticated requests 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -383,7 +394,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -391,6 +401,9 @@ Object { exports[`RenderingService preboot() render() renders "core" page with global settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -463,7 +476,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -471,6 +483,9 @@ Object { exports[`RenderingService preboot() render() renders "core" with excluded global user settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -539,7 +554,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -547,6 +561,9 @@ Object { exports[`RenderingService preboot() render() renders "core" with excluded user settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -615,7 +632,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -623,6 +639,9 @@ Object { exports[`RenderingService setup() render() renders "core" CDN url injected 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -700,7 +719,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -708,6 +726,9 @@ Object { exports[`RenderingService setup() render() renders "core" page 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -776,7 +797,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -784,6 +804,9 @@ Object { exports[`RenderingService setup() render() renders "core" page driven by settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -861,7 +884,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -869,6 +891,9 @@ Object { exports[`RenderingService setup() render() renders "core" page for blank basepath 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "", "branch": Any, @@ -942,7 +967,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -950,6 +974,9 @@ Object { exports[`RenderingService setup() render() renders "core" page for unauthenticated requests 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -1018,7 +1045,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -1026,6 +1052,9 @@ Object { exports[`RenderingService setup() render() renders "core" page with global settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -1103,7 +1132,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -1111,6 +1139,9 @@ Object { exports[`RenderingService setup() render() renders "core" with excluded global user settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -1184,7 +1215,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; @@ -1192,6 +1222,9 @@ Object { exports[`RenderingService setup() render() renders "core" with excluded user settings 1`] = ` Object { "anonymousStatusPage": false, + "apmConfig": Object { + "stubApmConfig": true, + }, "assetsHrefBase": "/mock-server-basepath", "basePath": "/mock-server-basepath", "branch": Any, @@ -1265,7 +1298,6 @@ Object { "version": "v8", }, "uiPlugins": Array [], - "vars": Object {}, "version": Any, } `; diff --git a/packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.mocks.ts b/packages/core/rendering/core-rendering-server-internal/src/get_apm_config.test.mocks.ts similarity index 100% rename from packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.mocks.ts rename to packages/core/rendering/core-rendering-server-internal/src/get_apm_config.test.mocks.ts diff --git a/packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.ts b/packages/core/rendering/core-rendering-server-internal/src/get_apm_config.test.ts similarity index 100% rename from packages/core/http/core-http-resources-server-internal/src/get_apm_config.test.ts rename to packages/core/rendering/core-rendering-server-internal/src/get_apm_config.test.ts diff --git a/packages/core/http/core-http-resources-server-internal/src/get_apm_config.ts b/packages/core/rendering/core-rendering-server-internal/src/get_apm_config.ts similarity index 100% rename from packages/core/http/core-http-resources-server-internal/src/get_apm_config.ts rename to packages/core/rendering/core-rendering-server-internal/src/get_apm_config.ts diff --git a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.mocks.ts b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.mocks.ts index e7d95312ed56a..96807a64b9560 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.mocks.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.mocks.ts @@ -28,3 +28,10 @@ jest.doMock('./render_utils', () => ({ getScriptPaths: getScriptPathsMock, getBrowserLoggingConfig: getBrowserLoggingConfigMock, })); + +export const getApmConfigMock = jest.fn(); +jest.doMock('./get_apm_config', () => { + return { + getApmConfig: getApmConfigMock, + }; +}); diff --git a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts index b07b8a1cd6fa1..9adf0a0ea3d69 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.test.ts @@ -14,6 +14,7 @@ import { getThemeStylesheetPathsMock, getScriptPathsMock, getBrowserLoggingConfigMock, + getApmConfigMock, } from './rendering_service.test.mocks'; import { load } from 'cheerio'; @@ -259,6 +260,25 @@ function renderTestCases( expect(data.logging).toEqual(loggingConfig); }); + it('renders "core" with APM config injected', async () => { + const someApmConfig = { someConfig: 9000 }; + getApmConfigMock.mockReturnValue(someApmConfig); + + const request = createKibanaRequest(); + + const [render] = await getRender(); + const content = await render(createKibanaRequest(), uiSettings, { + isAnonymousPage: false, + }); + + expect(getApmConfigMock).toHaveBeenCalledTimes(1); + expect(getApmConfigMock).toHaveBeenCalledWith(request.url.pathname); + + const dom = load(content); + const data = JSON.parse(dom('kbn-injected-metadata').attr('data') ?? '""'); + expect(data.apmConfig).toEqual(someApmConfig); + }); + it('use the correct translation url when CDN is enabled', async () => { const userSettings = { 'theme:darkMode': { userValue: true } }; uiSettings.client.getUserProvided.mockResolvedValue(userSettings); @@ -511,6 +531,7 @@ describe('RenderingService', () => { getThemeStylesheetPathsMock.mockReturnValue(['/style-1.css', '/style-2.css']); getScriptPathsMock.mockReturnValue(['/script-1.js']); getBrowserLoggingConfigMock.mockReset().mockReturnValue({}); + getApmConfigMock.mockReset().mockReturnValue({ stubApmConfig: true }); }); describe('preboot()', () => { diff --git a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx index 7a7e6e56fb49f..4b7e75ea9fb84 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx +++ b/packages/core/rendering/core-rendering-server-internal/src/rendering_service.tsx @@ -42,6 +42,7 @@ import { getBrowserLoggingConfig, } from './render_utils'; import { filterUiPlugins } from './filter_ui_plugins'; +import { getApmConfig } from './get_apm_config'; import type { InternalRenderingRequestHandlerContext } from './internal_types'; type RenderOptions = @@ -121,7 +122,7 @@ export class RenderingService { client: IUiSettingsClient; globalClient: IUiSettingsClient; }, - { isAnonymousPage = false, vars, includeExposedConfigKeys }: IRenderOptions = {} + { isAnonymousPage = false, includeExposedConfigKeys }: IRenderOptions = {} ) { const { elasticsearch, http, uiPlugins, status, customBranding, userSettings, i18n } = renderOptions; @@ -221,6 +222,7 @@ export class RenderingService { translationsUrl = `${serverBasePath}/translations/${translationHash}/${locale}.json`; } + const apmConfig = getApmConfig(request.url.pathname); const filteredPlugins = filterUiPlugins({ uiPlugins, isAnonymousPage }); const bootstrapScript = isAnonymousPage ? 'bootstrap-anonymous.js' : 'bootstrap.js'; const metadata: RenderingMetadata = { @@ -249,6 +251,7 @@ export class RenderingService { logging: loggingConfig, env, clusterInfo, + apmConfig, anonymousStatusPage: status?.isStatusPageAnonymous() ?? false, i18n: { translationsUrl, @@ -268,7 +271,6 @@ export class RenderingService { }, csp: { warnLegacyBrowsers: http.csp.warnLegacyBrowsers }, externalUrl: http.externalUrl, - vars: vars ?? {}, uiPlugins: await Promise.all( filteredPlugins.map(async ([id, plugin]) => { const { browserConfig, exposedConfigKeys } = await getUiConfig(uiPlugins, id); diff --git a/packages/core/rendering/core-rendering-server-internal/src/types.ts b/packages/core/rendering/core-rendering-server-internal/src/types.ts index ed6a45d071423..98887a9f9da29 100644 --- a/packages/core/rendering/core-rendering-server-internal/src/types.ts +++ b/packages/core/rendering/core-rendering-server-internal/src/types.ts @@ -64,13 +64,6 @@ export interface IRenderOptions { */ isAnonymousPage?: boolean; - /** - * Inject custom vars into the page metadata. - * @deprecated for legacy use only. Can be removed when https://github.com/elastic/kibana/issues/127733 is done. - * @internal - */ - vars?: Record; - /** * @internal * This is only used for integration tests that allow us to verify which config keys are exposed to the browser. diff --git a/packages/core/rendering/core-rendering-server-internal/tsconfig.json b/packages/core/rendering/core-rendering-server-internal/tsconfig.json index e306dca24059c..2689069f79d79 100644 --- a/packages/core/rendering/core-rendering-server-internal/tsconfig.json +++ b/packages/core/rendering/core-rendering-server-internal/tsconfig.json @@ -44,6 +44,7 @@ "@kbn/core-i18n-server", "@kbn/core-i18n-server-internal", "@kbn/core-i18n-server-mocks", + "@kbn/apm-config-loader", ], "exclude": [ "target/**/*", diff --git a/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts b/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts index c1ca8cb752d2d..f7a0685967ba8 100644 --- a/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts +++ b/packages/core/root/core-root-browser-internal/src/kbn_bootstrap.ts @@ -7,6 +7,7 @@ */ import { i18n } from '@kbn/i18n'; +import type { InjectedMetadata } from '@kbn/core-injected-metadata-common-internal'; import { KBN_LOAD_MARKS } from './events'; import { CoreSystem } from './core_system'; import { ApmSystem } from './apm_system'; @@ -19,12 +20,15 @@ export async function __kbnBootstrap__() { detail: LOAD_BOOTSTRAP_START, }); - const injectedMetadata = JSON.parse( + const injectedMetadata: InjectedMetadata = JSON.parse( document.querySelector('kbn-injected-metadata')!.getAttribute('data')! ); let i18nError: Error | undefined; - const apmSystem = new ApmSystem(injectedMetadata.vars.apmConfig, injectedMetadata.basePath); + const apmSystem = new ApmSystem( + injectedMetadata.apmConfig ?? undefined, + injectedMetadata.basePath + ); await Promise.all([ // eslint-disable-next-line no-console diff --git a/packages/core/root/core-root-browser-internal/tsconfig.json b/packages/core/root/core-root-browser-internal/tsconfig.json index 152c7d3683e38..e576ecf8cf920 100644 --- a/packages/core/root/core-root-browser-internal/tsconfig.json +++ b/packages/core/root/core-root-browser-internal/tsconfig.json @@ -66,6 +66,7 @@ "@kbn/core-security-browser-internal", "@kbn/core-user-profile-browser-mocks", "@kbn/core-user-profile-browser-internal", + "@kbn/core-injected-metadata-common-internal", ], "exclude": [ "target/**/*", diff --git a/packages/core/status/core-status-server-internal/src/routes/status.ts b/packages/core/status/core-status-server-internal/src/routes/status.ts index 4b243240321d7..45e6ff8ceb5d5 100644 --- a/packages/core/status/core-status-server-internal/src/routes/status.ts +++ b/packages/core/status/core-status-server-internal/src/routes/status.ts @@ -119,9 +119,11 @@ export const registerStatusRoute = ({ }, response: { 200: { + description: 'Overall status is OK and Kibana should be functioning normally.', body: statusResponse, }, 503: { + description: `Kibana or some of it's essential services are unavailable. Kibana may be degraded or unavailable.`, body: statusResponse, }, }, diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 98f5ba5d8b5da..fef52545f8d2f 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -196,9 +196,11 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D crawlerOverview: `${ENTERPRISE_SEARCH_DOCS}crawler.html`, deployTrainedModels: `${MACHINE_LEARNING_DOCS}ml-nlp-deploy-models.html`, documentLevelSecurity: `${ELASTICSEARCH_DOCS}document-level-security.html`, + e5Model: `${MACHINE_LEARNING_DOCS}ml-nlp-e5.html`, elser: `${ELASTICSEARCH_DOCS}semantic-search-elser.html`, engines: `${ENTERPRISE_SEARCH_DOCS}engines.html`, indexApi: `${ELASTICSEARCH_DOCS}docs-index_.html`, + inferenceApiCreate: `${ELASTICSEARCH_DOCS}put-inference-api.html`, ingestionApis: `${ELASTICSEARCH_DOCS}search-with-elasticsearch.html`, ingestPipelines: `${ELASTICSEARCH_DOCS}ingest-pipeline-search.html`, knnSearch: `${ELASTICSEARCH_DOCS}knn-search.html`, @@ -216,6 +218,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D searchLabs: `${SEARCH_LABS_URL}`, searchLabsRepo: `${SEARCH_LABS_REPO}`, searchTemplates: `${ELASTICSEARCH_DOCS}search-template.html`, + semanticTextField: `${ELASTICSEARCH_DOCS}semantic-text.html`, start: `${ENTERPRISE_SEARCH_DOCS}start.html`, supportedNlpModels: `${MACHINE_LEARNING_DOCS}ml-nlp-model-ref.html`, syncRules: `${ENTERPRISE_SEARCH_DOCS}sync-rules.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index c6b1b753ce883..d271ed918327a 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -160,9 +160,11 @@ export interface DocLinks { readonly crawlerOverview: string; readonly deployTrainedModels: string; readonly documentLevelSecurity: string; + readonly e5Model: string; readonly elser: string; readonly engines: string; readonly indexApi: string; + readonly inferenceApiCreate: string; readonly ingestionApis: string; readonly ingestPipelines: string; readonly knnSearch: string; @@ -180,6 +182,7 @@ export interface DocLinks { readonly searchLabs: string; readonly searchLabsRepo: string; readonly searchTemplates: string; + readonly semanticTextField: string; readonly start: string; readonly supportedNlpModels: string; readonly syncRules: string; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts index 2d81fe794193b..a37756ecd0866 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts @@ -116,8 +116,8 @@ describe('autocomplete.suggest', () => { test('when typing inside function left paren', async () => { const { assertSuggestions } = await setup(); const expected = [ - ...getFieldNamesByType(['number', 'date', 'boolean']), - ...getFunctionSignaturesByReturnType('stats', ['number', 'date', 'boolean'], { + ...getFieldNamesByType(['number', 'date', 'boolean', 'ip']), + ...getFunctionSignaturesByReturnType('stats', ['number', 'date', 'boolean', 'ip'], { evalMath: true, }), ]; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts index 657b5de67896e..2ceb7ae2cd45a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts @@ -247,15 +247,12 @@ export function createCustomCallbackMocks( }; } -export function createSuggestContext(text: string, triggerCharacter?: string) { +export function createCompletionContext(triggerCharacter?: string) { if (triggerCharacter) { return { triggerCharacter, triggerKind: 1 }; // any number is fine here } - const foundTriggerCharIndexes = triggerCharacters.map((char) => text.lastIndexOf(char)); - const maxIndex = Math.max(...foundTriggerCharIndexes); return { - triggerCharacter: text[maxIndex], - triggerKind: 1, + triggerKind: 0, }; } 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 687684f6fcf34..26e0159e70102 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts @@ -23,7 +23,7 @@ import { getLiteralsByType, getDateLiteralsByFieldType, createCustomCallbackMocks, - createSuggestContext, + createCompletionContext, getPolicyFields, } from './__tests__/helpers'; @@ -31,31 +31,30 @@ describe('autocomplete', () => { type TestArgs = [ string, string[], - (string | number)?, + string?, + number?, Parameters? ]; - const testSuggestionsFn = ( + const _testSuggestionsFn = ( + { only, skip }: { only?: boolean; skip?: boolean } = {}, statement: string, expected: string[], - triggerCharacter: string | number = '', + triggerCharacter?: string, + _offset?: number, customCallbacksArgs: Parameters = [ undefined, undefined, undefined, - ], - { only, skip }: { only?: boolean; skip?: boolean } = {} + ] ) => { - const triggerCharacterString = - triggerCharacter == null || typeof triggerCharacter === 'string' - ? triggerCharacter - : statement[triggerCharacter + 1]; - const context = createSuggestContext(statement, triggerCharacterString); - const offset = - typeof triggerCharacter === 'string' - ? statement.lastIndexOf(context.triggerCharacter) + 1 - : triggerCharacter; + const context = createCompletionContext(triggerCharacter); const testFn = only ? test.only : skip ? test.skip : test; + const offset = _offset + ? _offset + : triggerCharacter + ? statement.lastIndexOf(triggerCharacter) + 1 + : statement.length; testFn(statement, async () => { const callbackMocks = createCustomCallbackMocks(...customCallbacksArgs); @@ -79,24 +78,12 @@ describe('autocomplete', () => { // DO NOT CHANGE THE NAME OF THIS FUNCTION WITHOUT ALSO CHANGING // THE LINTER RULE IN packages/kbn-eslint-config/typescript.js // - const testSuggestions = Object.assign(testSuggestionsFn, { + const testSuggestions = Object.assign(_testSuggestionsFn.bind(null, {}), { skip: (...args: TestArgs) => { - const paddingArgs = ['', [undefined, undefined, undefined]].slice(args.length - 2); - return testSuggestionsFn( - ...((args.length > 1 ? [...args, ...paddingArgs] : args) as TestArgs), - { - skip: true, - } - ); + return _testSuggestionsFn({ skip: true }, ...args); }, only: (...args: TestArgs) => { - const paddingArgs = ['', [undefined, undefined, undefined]].slice(args.length - 2); - return testSuggestionsFn( - ...((args.length > 1 ? [...args, ...paddingArgs] : args) as TestArgs), - { - only: true, - } - ); + return _testSuggestionsFn({ only: true }, ...args); }, }); @@ -223,7 +210,8 @@ describe('autocomplete', () => { testSuggestions( 'from a | stats a=avg(numberField) | where numberField ', [], - '', + undefined, + undefined, // make the fields suggest aware of the previous STATS, leave the other callbacks untouched [[{ name: 'a', type: 'number' }], undefined, undefined] ); @@ -277,6 +265,7 @@ describe('autocomplete', () => { ), ...getFunctionSignaturesByReturnType('where', 'number', { evalMath: true }), ], + undefined, 54 // after the first suggestions ); testSuggestions( @@ -287,50 +276,61 @@ describe('autocomplete', () => { ), ...getFunctionSignaturesByReturnType('where', 'number', { evalMath: true }), ], + undefined, 58 // after the first suggestions ); }); - for (const command of ['grok', 'dissect']) { - describe(command, () => { - const constantPattern = command === 'grok' ? '"%{WORD:firstWord}"' : '"%{firstWord}"'; - const subExpressions = [ - '', - `${command} stringField |`, - `${command} stringField ${constantPattern} |`, - `dissect stringField ${constantPattern} append_separator = ":" |`, - ]; - if (command === 'grok') { - subExpressions.push(`dissect stringField ${constantPattern} |`); - } - for (const subExpression of subExpressions) { - testSuggestions(`from a | ${subExpression} ${command} `, getFieldNamesByType('string')); - testSuggestions(`from a | ${subExpression} ${command} stringField `, [constantPattern]); - testSuggestions( - `from a | ${subExpression} ${command} stringField ${constantPattern} `, - (command === 'dissect' ? ['APPEND_SEPARATOR = $0'] : []).concat(['|']) - ); - if (command === 'dissect') { - testSuggestions( - `from a | ${subExpression} ${command} stringField ${constantPattern} append_separator = `, - ['":"', '";"'] - ); - testSuggestions( - `from a | ${subExpression} ${command} stringField ${constantPattern} append_separator = ":" `, - ['|'] - ); - } - } - }); - } + describe('grok', () => { + const constantPattern = '"%{WORD:firstWord}"'; + const subExpressions = [ + '', + `grok stringField |`, + `grok stringField ${constantPattern} |`, + `dissect stringField ${constantPattern} append_separator = ":" |`, + `dissect stringField ${constantPattern} |`, + ]; + for (const subExpression of subExpressions) { + testSuggestions(`from a | ${subExpression} grok `, getFieldNamesByType('string')); + testSuggestions(`from a | ${subExpression} grok stringField `, [constantPattern], ' '); + testSuggestions(`from a | ${subExpression} grok stringField ${constantPattern} `, ['|']); + } + }); + + describe('dissect', () => { + const constantPattern = '"%{firstWord}"'; + const subExpressions = [ + '', + `dissect stringField |`, + `dissect stringField ${constantPattern} |`, + `dissect stringField ${constantPattern} append_separator = ":" |`, + ]; + for (const subExpression of subExpressions) { + testSuggestions(`from a | ${subExpression} dissect `, getFieldNamesByType('string')); + testSuggestions(`from a | ${subExpression} dissect stringField `, [constantPattern], ' '); + testSuggestions( + `from a | ${subExpression} dissect stringField ${constantPattern} `, + ['APPEND_SEPARATOR = $0', '|'], + ' ' + ); + testSuggestions( + `from a | ${subExpression} dissect stringField ${constantPattern} append_separator = `, + ['":"', '";"'] + ); + testSuggestions( + `from a | ${subExpression} dissect stringField ${constantPattern} append_separator = ":" `, + ['|'] + ); + } + }); describe('sort', () => { testSuggestions('from a | sort ', [ ...getFieldNamesByType('any'), ...getFunctionSignaturesByReturnType('sort', 'any', { evalMath: true }), ]); - testSuggestions('from a | sort stringField ', ['asc', 'desc', ',', '|']); - testSuggestions('from a | sort stringField desc ', ['nulls first', 'nulls last', ',', '|']); + testSuggestions('from a | sort stringField ', ['ASC', 'DESC', ',', '|']); + testSuggestions('from a | sort stringField desc ', ['NULLS FIRST', 'NULLS LAST', ',', '|']); // @TODO: improve here // testSuggestions('from a | sort stringField desc ', ['first', 'last']); }); @@ -347,7 +347,7 @@ describe('autocomplete', () => { describe('rename', () => { testSuggestions('from a | rename ', getFieldNamesByType('any')); - testSuggestions('from a | rename stringField ', ['AS $0']); + testSuggestions('from a | rename stringField ', ['AS $0'], ' '); testSuggestions('from a | rename stringField as ', ['var0']); }); @@ -408,10 +408,11 @@ describe('autocomplete', () => { 'kubernetes.something.something', ]); testSuggestions(`from a ${prevCommand}| enrich policy on b `, ['WITH $0', ',', '|']); - testSuggestions(`from a ${prevCommand}| enrich policy on b with `, [ - 'var0 =', - ...getPolicyFields('policy'), - ]); + testSuggestions( + `from a ${prevCommand}| enrich policy on b with `, + ['var0 =', ...getPolicyFields('policy')], + ' ' + ); testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 `, ['= $0', ',', '|']); testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = `, [ ...getPolicyFields('policy'), @@ -433,10 +434,11 @@ describe('autocomplete', () => { `from a ${prevCommand}| enrich policy on b with var0 = stringField, var1 = `, [...getPolicyFields('policy')] ); - testSuggestions(`from a ${prevCommand}| enrich policy with `, [ - 'var0 =', - ...getPolicyFields('policy'), - ]); + testSuggestions( + `from a ${prevCommand}| enrich policy with `, + ['var0 =', ...getPolicyFields('policy')], + ' ' + ); testSuggestions(`from a ${prevCommand}| enrich policy with stringField `, ['= $0', ',', '|']); } }); @@ -512,11 +514,13 @@ describe('autocomplete', () => { ); testSuggestions( 'from a | eval raund(5, ', // note the typo in round - [] + [], + ' ' ); testSuggestions( 'from a | eval var0 = raund(5, ', // note the typo in round - [] + [], + ' ' ); testSuggestions('from a | eval a=round(numberField) ', [ ',', @@ -525,18 +529,26 @@ describe('autocomplete', () => { 'number', ]), ]); - testSuggestions('from a | eval a=round(numberField, ', [ - ...getFieldNamesByType('number'), - ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ - 'round', - ]), - ]); - testSuggestions('from a | eval round(numberField, ', [ - ...getFieldNamesByType('number'), - ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ - 'round', - ]), - ]); + testSuggestions( + 'from a | eval a=round(numberField, ', + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ + 'round', + ]), + ], + ' ' + ); + testSuggestions( + 'from a | eval round(numberField, ', + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ + 'round', + ]), + ], + ' ' + ); testSuggestions('from a | eval a=round(numberField),', [ 'var0 =', ...getFieldNamesByType('any'), @@ -571,6 +583,7 @@ describe('autocomplete', () => { ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), ], ' ', + undefined, // make aware EVAL of the previous STATS command [[], undefined, undefined] ); @@ -592,6 +605,7 @@ describe('autocomplete', () => { ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), ], ' ', + undefined, // make aware EVAL of the previous STATS command with the buggy field name from expression [[{ name: 'avg_numberField_', type: 'number' }], undefined, undefined] ); @@ -604,6 +618,7 @@ describe('autocomplete', () => { ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), ], ' ', + undefined, // make aware EVAL of the previous STATS command with the buggy field name from expression [ [ @@ -631,19 +646,27 @@ describe('autocomplete', () => { 'concat', ]).map((v) => `${v},`), ]); - testSuggestions('from a | eval a=concat(stringField, ', [ - ...getFieldNamesByType('string'), - ...getFunctionSignaturesByReturnType('eval', 'string', { evalMath: true }, undefined, [ - 'concat', - ]), - ]); + testSuggestions( + 'from a | eval a=concat(stringField, ', + [ + ...getFieldNamesByType('string'), + ...getFunctionSignaturesByReturnType('eval', 'string', { evalMath: true }, undefined, [ + 'concat', + ]), + ], + ' ' + ); // test that the arg type is correct after minParams - testSuggestions('from a | eval a=cidr_match(ipField, stringField,', [ - ...getFieldNamesByType('string'), - ...getFunctionSignaturesByReturnType('eval', 'string', { evalMath: true }, undefined, [ - 'cidr_match', - ]), - ]); + testSuggestions( + 'from a | eval a=cidr_match(ipField, stringField, ', + [ + ...getFieldNamesByType('string'), + ...getFunctionSignaturesByReturnType('eval', 'string', { evalMath: true }, undefined, [ + 'cidr_match', + ]), + ], + ' ' + ); // test that comma is correctly added to the suggestions if minParams is not reached yet testSuggestions('from a | eval a=cidr_match( ', [ ...getFieldNamesByType('ip').map((v) => `${v},`), @@ -651,12 +674,16 @@ describe('autocomplete', () => { 'cidr_match', ]).map((v) => `${v},`), ]); - testSuggestions('from a | eval a=cidr_match(ipField, ', [ - ...getFieldNamesByType('string'), - ...getFunctionSignaturesByReturnType('eval', 'string', { evalMath: true }, undefined, [ - 'cidr_match', - ]), - ]); + testSuggestions( + 'from a | eval a=cidr_match(ipField, ', + [ + ...getFieldNamesByType('string'), + ...getFunctionSignaturesByReturnType('eval', 'string', { evalMath: true }, undefined, [ + 'cidr_match', + ]), + ], + ' ' + ); // test deep function nesting suggestions (and check that the same function is not suggested) // round(round( // round(round(round( @@ -684,6 +711,7 @@ describe('autocomplete', () => { 'number', ]), ], + undefined, 38 /* " " after abs(b) */ ); testSuggestions( @@ -694,6 +722,7 @@ describe('autocomplete', () => { 'abs', ]), ], + undefined, 26 /* b column in abs */ ); @@ -747,7 +776,8 @@ describe('autocomplete', () => { ...getLiteralsByType(getTypesFromParamDefs(constantOnlyParamDefs)).map((d) => requiresMoreArgs ? `${d},` : d ), - ] + ], + ' ' ); testSuggestions( `from a | eval var0 = ${fn.name}(${Array(i).fill('field').join(', ')}${ @@ -772,7 +802,8 @@ describe('autocomplete', () => { ...getLiteralsByType(getTypesFromParamDefs(constantOnlyParamDefs)).map((d) => requiresMoreArgs ? `${d},` : d ), - ] + ], + ' ' ); } }); @@ -780,19 +811,23 @@ describe('autocomplete', () => { } } - testSuggestions('from a | eval var0 = bucket(@timestamp,', getUnitDuration(1)); + testSuggestions('from a | eval var0 = bucket(@timestamp, ', getUnitDuration(1), ' '); describe('date math', () => { const dateSuggestions = timeUnitsToSuggest.map(({ name }) => name); // If a literal number is detected then suggest also date period keywords - testSuggestions('from a | eval a = 1 ', [ - ...dateSuggestions, - ',', - '|', - ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ - 'number', - ]), - ]); + testSuggestions( + 'from a | eval a = 1 ', + [ + ...dateSuggestions, + ',', + '|', + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ + 'number', + ]), + ], + ' ' + ); testSuggestions('from a | eval a = 1 year ', [ ',', '|', @@ -800,20 +835,28 @@ describe('autocomplete', () => { 'time_interval', ]), ]); - testSuggestions('from a | eval a = 1 day + 2 ', [ - ...dateSuggestions, - ',', - '|', - ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ - 'number', - ]), - ]); - testSuggestions('from a | eval 1 day + 2 ', [ - ...dateSuggestions, - ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ - 'number', - ]), - ]); + testSuggestions( + 'from a | eval a = 1 day + 2 ', + [ + ...dateSuggestions, + ',', + '|', + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ + 'number', + ]), + ], + ' ' + ); + testSuggestions( + 'from a | eval 1 day + 2 ', + [ + ...dateSuggestions, + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ + 'number', + ]), + ], + ' ' + ); testSuggestions( 'from a | eval var0=date_trunc()', [ @@ -826,10 +869,11 @@ describe('autocomplete', () => { ], '(' ); - testSuggestions('from a | eval var0=date_trunc(2 )', [ - ...dateSuggestions.map((t) => `${t},`), - ',', - ]); + testSuggestions( + 'from a | eval var0=date_trunc(2 )', + [...dateSuggestions.map((t) => `${t},`), ','], + ' ' + ); }); }); @@ -838,7 +882,7 @@ describe('autocomplete', () => { const callbackMocks = createCustomCallbackMocks(undefined, undefined, undefined); const statement = 'from a | drop stringField | eval var0 = abs(numberField) '; const triggerOffset = statement.lastIndexOf(' '); - const context = createSuggestContext(statement, statement[triggerOffset]); + const context = createCompletionContext(statement[triggerOffset]); await suggest( statement, triggerOffset + 1, @@ -854,7 +898,7 @@ describe('autocomplete', () => { const callbackMocks = createCustomCallbackMocks(undefined, undefined, undefined); const statement = 'from a | drop | eval var0 = abs(numberField) '; const triggerOffset = statement.lastIndexOf('p') + 1; // drop - const context = createSuggestContext(statement, statement[triggerOffset]); + const context = createCompletionContext(statement[triggerOffset]); await suggest( statement, triggerOffset + 1, @@ -870,7 +914,7 @@ describe('autocomplete', () => { function getSuggestionsFor(statement: string) { const callbackMocks = createCustomCallbackMocks(undefined, undefined, undefined); const triggerOffset = statement.lastIndexOf(' ') + 1; // drop - const context = createSuggestContext(statement, statement[triggerOffset]); + const context = createCompletionContext(statement[triggerOffset]); return suggest( statement, triggerOffset + 1, diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts index 69f8f76807daf..a27b8a68a9be0 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts @@ -112,6 +112,10 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ params: [{ name: 'column', type: 'boolean', noNestingFunctions: true }], returnType: 'boolean', }, + { + params: [{ name: 'column', type: 'ip', noNestingFunctions: true }], + returnType: 'ip', + }, ], examples: [`from index | stats result = max(field)`, `from index | stats max(field)`], }, @@ -135,6 +139,10 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ params: [{ name: 'column', type: 'boolean', noNestingFunctions: true }], returnType: 'boolean', }, + { + params: [{ name: 'column', type: 'ip', noNestingFunctions: true }], + returnType: 'ip', + }, ], examples: [`from index | stats result = min(field)`, `from index | stats min(field)`], }, diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts index 2485b32837a5b..9bbc8a5b903d2 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts @@ -372,8 +372,8 @@ export const commandDefinitions: CommandDefinition[] = [ multipleParams: true, params: [ { name: 'expression', type: 'any' }, - { name: 'direction', type: 'string', optional: true, values: ['asc', 'desc'] }, - { name: 'nulls', type: 'string', optional: true, values: ['nulls first', 'nulls last'] }, + { name: 'direction', type: 'string', optional: true, values: ['ASC', 'DESC'] }, + { name: 'nulls', type: 'string', optional: true, values: ['NULLS FIRST', 'NULLS LAST'] }, ], }, }, 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 a2cc5bf55ff35..65dc30e79064b 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 @@ -24201,6 +24201,58 @@ ], "warning": [] }, + { + "query": "from a_index | stats var = max(ipField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | stats max(ipField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where max(ipField)", + "error": [ + "WHERE does not support function max" + ], + "warning": [] + }, + { + "query": "from a_index | where max(ipField) > 0", + "error": [ + "WHERE does not support function max" + ], + "warning": [] + }, + { + "query": "from a_index | eval var = max(ipField)", + "error": [ + "EVAL does not support function max" + ], + "warning": [] + }, + { + "query": "from a_index | eval var = max(ipField) > 0", + "error": [ + "EVAL does not support function max" + ], + "warning": [] + }, + { + "query": "from a_index | eval max(ipField)", + "error": [ + "EVAL does not support function max" + ], + "warning": [] + }, + { + "query": "from a_index | eval max(ipField) > 0", + "error": [ + "EVAL does not support function max" + ], + "warning": [] + }, { "query": "from a_index | stats var = min(numberField)", "error": [], @@ -24526,6 +24578,58 @@ ], "warning": [] }, + { + "query": "from a_index | stats var = min(ipField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | stats min(ipField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where min(ipField)", + "error": [ + "WHERE does not support function min" + ], + "warning": [] + }, + { + "query": "from a_index | where min(ipField) > 0", + "error": [ + "WHERE does not support function min" + ], + "warning": [] + }, + { + "query": "from a_index | eval var = min(ipField)", + "error": [ + "EVAL does not support function min" + ], + "warning": [] + }, + { + "query": "from a_index | eval var = min(ipField) > 0", + "error": [ + "EVAL does not support function min" + ], + "warning": [] + }, + { + "query": "from a_index | eval min(ipField)", + "error": [ + "EVAL does not support function min" + ], + "warning": [] + }, + { + "query": "from a_index | eval min(ipField) > 0", + "error": [ + "EVAL does not support function min" + ], + "warning": [] + }, { "query": "from a_index | stats var = count(stringField)", "error": [], 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 afbd3db5458bb..e9a707fd85e98 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -9202,6 +9202,32 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval max(booleanField) > 0', [ 'EVAL does not support function max', ]); + testErrorsAndWarnings('from a_index | stats var = max(ipField)', []); + testErrorsAndWarnings('from a_index | stats max(ipField)', []); + + testErrorsAndWarnings('from a_index | where max(ipField)', [ + 'WHERE does not support function max', + ]); + + testErrorsAndWarnings('from a_index | where max(ipField) > 0', [ + 'WHERE does not support function max', + ]); + + testErrorsAndWarnings('from a_index | eval var = max(ipField)', [ + 'EVAL does not support function max', + ]); + + testErrorsAndWarnings('from a_index | eval var = max(ipField) > 0', [ + 'EVAL does not support function max', + ]); + + testErrorsAndWarnings('from a_index | eval max(ipField)', [ + 'EVAL does not support function max', + ]); + + testErrorsAndWarnings('from a_index | eval max(ipField) > 0', [ + 'EVAL does not support function max', + ]); }); describe('min', () => { @@ -9374,6 +9400,32 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval min(booleanField) > 0', [ 'EVAL does not support function min', ]); + testErrorsAndWarnings('from a_index | stats var = min(ipField)', []); + testErrorsAndWarnings('from a_index | stats min(ipField)', []); + + testErrorsAndWarnings('from a_index | where min(ipField)', [ + 'WHERE does not support function min', + ]); + + testErrorsAndWarnings('from a_index | where min(ipField) > 0', [ + 'WHERE does not support function min', + ]); + + testErrorsAndWarnings('from a_index | eval var = min(ipField)', [ + 'EVAL does not support function min', + ]); + + testErrorsAndWarnings('from a_index | eval var = min(ipField) > 0', [ + 'EVAL does not support function min', + ]); + + testErrorsAndWarnings('from a_index | eval min(ipField)', [ + 'EVAL does not support function min', + ]); + + testErrorsAndWarnings('from a_index | eval min(ipField) > 0', [ + 'EVAL does not support function min', + ]); }); describe('count', () => { diff --git a/packages/kbn-monaco/src/esql/lib/esql_theme.ts b/packages/kbn-monaco/src/esql/lib/esql_theme.ts index a6907847c7ade..511fcbf9114f4 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_theme.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_theme.ts @@ -78,14 +78,13 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ 'as', 'expr_ws', 'limit', - 'nulls_ordering_direction', - 'nulls_ordering', 'null', 'enrich', 'on', 'with', 'asc', 'desc', + 'nulls_order', ], euiThemeVars.euiColorAccentText, true // isBold diff --git a/packages/kbn-monaco/src/esql/lib/esql_token_helpers.ts b/packages/kbn-monaco/src/esql/lib/esql_token_helpers.ts index e77b9ccfe6e40..a43360f48e9c9 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_token_helpers.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_token_helpers.ts @@ -13,9 +13,7 @@ function nonNullable(value: T | undefined): value is T { return value != null; } -export function enrichTokensWithFunctionsMetadata( - tokens: monaco.languages.IToken[] -): monaco.languages.IToken[] { +export function addFunctionTokens(tokens: monaco.languages.IToken[]): monaco.languages.IToken[] { // need to trim spaces as "abs (arg)" is still valid as function const myTokensWithoutSpaces = tokens.filter( ({ scopes }) => scopes !== 'expr_ws' + ESQL_TOKEN_POSTFIX @@ -34,3 +32,18 @@ export function enrichTokensWithFunctionsMetadata( } return [...tokens]; } + +export function addNullsOrder(tokens: monaco.languages.IToken[]): void { + const nullsIndex = tokens.findIndex((token) => token.scopes === 'nulls' + ESQL_TOKEN_POSTFIX); + if ( + // did we find a "nulls"? + nullsIndex > -1 && + // is the next non-whitespace token an order? + ['first' + ESQL_TOKEN_POSTFIX, 'last' + ESQL_TOKEN_POSTFIX].includes( + tokens[nullsIndex + 2]?.scopes + ) + ) { + tokens[nullsIndex].scopes = 'nulls_order' + ESQL_TOKEN_POSTFIX; + tokens.splice(nullsIndex + 1, 2); + } +} diff --git a/packages/kbn-monaco/src/esql/lib/esql_tokens_provider.ts b/packages/kbn-monaco/src/esql/lib/esql_tokens_provider.ts index 378e86cbfb27d..d5cbdf4349b4c 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_tokens_provider.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_tokens_provider.ts @@ -15,7 +15,7 @@ import { ESQLLineTokens } from './esql_line_tokens'; import { ESQLState } from './esql_state'; import { ESQL_TOKEN_POSTFIX } from './constants'; -import { enrichTokensWithFunctionsMetadata } from './esql_token_helpers'; +import { addFunctionTokens, addNullsOrder } from './esql_token_helpers'; const EOF = -1; @@ -77,7 +77,8 @@ export class ESQLTokensProvider implements monaco.languages.TokensProvider { // special treatment for functions // the previous custom Kibana grammar baked functions directly as tokens, so highlight was easier // The ES grammar doesn't have the token concept of "function" - const tokensWithFunctions = enrichTokensWithFunctionsMetadata(myTokens); + const tokensWithFunctions = addFunctionTokens(myTokens); + addNullsOrder(tokensWithFunctions); return new ESQLLineTokens(tokensWithFunctions, prevState.getLineNumber() + 1); } diff --git a/packages/kbn-openapi-generator/redocly_linter/config.yaml b/packages/kbn-openapi-generator/redocly_linter/config.yaml index b423d9172b1c8..fc4ff630cc2bb 100644 --- a/packages/kbn-openapi-generator/redocly_linter/config.yaml +++ b/packages/kbn-openapi-generator/redocly_linter/config.yaml @@ -5,23 +5,24 @@ plugins: rules: spec: error - spec-strict-refs: warn + spec-strict-refs: error no-path-trailing-slash: error no-identical-paths: error - no-ambiguous-paths: warn + no-ambiguous-paths: error no-unresolved-refs: error no-enum-type-mismatch: error component-name-unique: error path-declaration-must-exist: error path-not-include-query: error - path-parameters-defined: warn - operation-description: warn operation-2xx-response: error - operation-4xx-response: warn operation-operationId: error operation-operationId-unique: error - operation-summary: warn operation-operationId-url-safe: error operation-parameters-unique: error - boolean-parameter-prefixes: warn extra-linter-rules-plugin/valid-x-modify: error + # Disable rules generating the majority of warnings. + # They will be handled separately. + # operation-description: warn + # operation-summary: warn + # operation-4xx-response: warn + # path-parameters-defined: warn diff --git a/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts b/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts index e7a5db404c5ca..90ba58720d3c2 100644 --- a/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts +++ b/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts @@ -96,6 +96,7 @@ it('builds a generated plugin into a viable archive', async () => { expect(files).toMatchInlineSnapshot(` Array [ + "kibana/fooTestPlugin/.i18nrc.json", "kibana/fooTestPlugin/common/index.js", "kibana/fooTestPlugin/kibana.json", "kibana/fooTestPlugin/node_modules/.yarn-integrity", diff --git a/packages/kbn-plugin-helpers/src/tasks/write_server_files.ts b/packages/kbn-plugin-helpers/src/tasks/write_server_files.ts index 362ef171cd9da..c5c61fbad90ff 100644 --- a/packages/kbn-plugin-helpers/src/tasks/write_server_files.ts +++ b/packages/kbn-plugin-helpers/src/tasks/write_server_files.ts @@ -32,6 +32,7 @@ export async function writeServerFiles({ vfs.src( [ 'kibana.json', + '.i18nrc.json', ...(plugin.manifest.server ? config.serverSourcePatterns || [ 'yarn.lock', diff --git a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap index 9a50453d972bd..acb8332a7f8f5 100644 --- a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap +++ b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap @@ -89,6 +89,7 @@ Object { }, }, }, + "description": undefined, }, }, "summary": "", @@ -215,6 +216,8 @@ Object { }, }, }, + "description": "OK response oas-test-version-1 +OK response oas-test-version-2", }, }, "summary": "versioned route", @@ -224,6 +227,34 @@ Object { }, }, "/foo/{id}/{path*}": Object { + "delete": Object { + "description": "route description", + "operationId": "/foo/{id}/{path*}#2", + "parameters": Array [ + Object { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": Object { + "default": "2023-10-31", + "enum": Array [ + "2023-10-31", + ], + "type": "string", + }, + }, + ], + "requestBody": undefined, + "responses": Object { + "200": Object { + "description": "good response", + }, + }, + "summary": "route summary", + "tags": Array [ + "bar", + ], + }, "get": Object { "description": "route description", "operationId": "/foo/{id}/{path*}#0", @@ -362,6 +393,7 @@ Object { }, }, }, + "description": undefined, }, }, "summary": "route summary", @@ -507,6 +539,7 @@ Object { }, }, }, + "description": undefined, }, }, "summary": "route summary", @@ -615,6 +648,7 @@ Object { }, }, }, + "description": undefined, }, }, "summary": "", @@ -691,6 +725,7 @@ Object { "schema": Object {}, }, }, + "description": undefined, }, }, "summary": "", @@ -728,6 +763,7 @@ Object { "schema": Object {}, }, }, + "description": undefined, }, }, "summary": "", diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts index 603517be4eda9..85d5f2401a230 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts @@ -121,6 +121,7 @@ export const sharedOas = { }, responses: { '200': { + description: 'OK response oas-test-version-1\nOK response oas-test-version-2', content: { 'application/json; Elastic-Api-Version=oas-test-version-1': { schema: { diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts index 29e09125b0a22..e519e1984fd23 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts @@ -45,7 +45,21 @@ describe('generateOpenApiDocument', () => { it('generates the expected OpenAPI document', () => { const [routers, versionedRouters] = createTestRouters({ - routers: { testRouter: { routes: [{ method: 'get' }, { method: 'post' }] } }, + routers: { + testRouter: { + routes: [ + { method: 'get' }, + { method: 'post' }, + { + method: 'delete', + validationSchemas: { + request: {}, + response: { [200]: { description: 'good response' } }, + }, + }, + ], + }, + }, versionedRouters: { testVersionedRouter: { routes: [{}] } }, }); expect( diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts index aeaf3aeb08a4b..f00ee68e6d86d 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts @@ -83,6 +83,7 @@ export const getVersionedRouterDefaults = (bodySchema?: RuntimeSchema) => ({ }, response: { [200]: { + description: 'OK response oas-test-version-1', body: () => schema.object( { fooResponseWithDescription: schema.string() }, @@ -101,6 +102,7 @@ export const getVersionedRouterDefaults = (bodySchema?: RuntimeSchema) => ({ request: { body: schema.object({ foo: schema.string() }) }, response: { [200]: { + description: 'OK response oas-test-version-2', body: () => schema.stream({ meta: { description: 'stream response' } }), bodyContentType: 'application/octet-stream', }, diff --git a/packages/kbn-router-to-openapispec/src/process_router.test.ts b/packages/kbn-router-to-openapispec/src/process_router.test.ts index 41850b31c5d46..e73506a574003 100644 --- a/packages/kbn-router-to-openapispec/src/process_router.test.ts +++ b/packages/kbn-router-to-openapispec/src/process_router.test.ts @@ -33,10 +33,12 @@ describe('extractResponses', () => { response: { 200: { bodyContentType: 'application/test+json', + description: 'OK response', body: () => schema.object({ bar: schema.number({ min: 1, max: 99 }) }), }, 404: { bodyContentType: 'application/test2+json', + description: 'Not Found response', body: () => schema.object({ ok: schema.literal(false) }), }, unsafe: { body: false }, @@ -45,6 +47,7 @@ describe('extractResponses', () => { }; expect(extractResponses(route, oasConverter)).toEqual({ 200: { + description: 'OK response', content: { 'application/test+json; Elastic-Api-Version=2023-10-31': { schema: { @@ -59,6 +62,7 @@ describe('extractResponses', () => { }, }, 404: { + description: 'Not Found response', content: { 'application/test2+json; Elastic-Api-Version=2023-10-31': { schema: { diff --git a/packages/kbn-router-to-openapispec/src/process_router.ts b/packages/kbn-router-to-openapispec/src/process_router.ts index 9437612211a92..aa40ee37d89ab 100644 --- a/packages/kbn-router-to-openapispec/src/process_router.ts +++ b/packages/kbn-router-to-openapispec/src/process_router.ts @@ -19,6 +19,7 @@ import { getPathParameters, getVersionedContentTypeString, getVersionedHeaderParam, + mergeResponseContent, prepareRoutes, } from './util'; import type { OperationIdCounter } from './operation_id_counter'; @@ -102,18 +103,23 @@ export const extractResponses = (route: InternalRouterRoute, converter: OasConve const contentType = extractContentType(route.options?.body); return Object.entries(validationSchemas).reduce( (acc, [statusCode, schema]) => { - const oasSchema = converter.convert(schema.body()); + const newContent = schema.body + ? { + [getVersionedContentTypeString( + SERVERLESS_VERSION_2023_10_31, + schema.bodyContentType ? [schema.bodyContentType] : contentType + )]: { + schema: converter.convert(schema.body()), + }, + } + : undefined; acc[statusCode] = { ...acc[statusCode], - content: { - ...((acc[statusCode] ?? {}) as OpenAPIV3.ResponseObject).content, - [getVersionedContentTypeString( - SERVERLESS_VERSION_2023_10_31, - schema.bodyContentType ? [schema.bodyContentType] : contentType - )]: { - schema: oasSchema, - }, - }, + description: schema.description!, + ...mergeResponseContent( + ((acc[statusCode] ?? {}) as OpenAPIV3.ResponseObject).content, + newContent + ), }; return acc; }, diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts index 04605ea431b14..5ae2b4ef746ca 100644 --- a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts +++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts @@ -20,60 +20,6 @@ import { extractVersionedRequestBodies, } from './process_versioned_router'; -const route: VersionedRouterRoute = { - path: '/foo', - method: 'get', - options: { - access: 'public', - options: { body: { access: ['application/test+json'] } as any }, - }, - handlers: [ - { - fn: jest.fn(), - options: { - version: '2023-10-31', - validate: () => ({ - request: { - body: schema.object({ foo: schema.string() }), - }, - response: { - 200: { - bodyContentType: 'application/test+json', - body: () => schema.object({ bar: schema.number({ min: 1, max: 99 }) }), - }, - 404: { - bodyContentType: 'application/test2+json', - body: () => schema.object({ ok: schema.literal(false) }), - }, - unsafe: { body: false }, - }, - }), - }, - }, - { - fn: jest.fn(), - options: { - version: '2024-12-31', - validate: () => ({ - request: { - body: schema.object({ foo2: schema.string() }), - }, - response: { - 200: { - bodyContentType: 'application/test+json', - body: () => schema.object({ bar2: schema.number({ min: 1, max: 99 }) }), - }, - 500: { - bodyContentType: 'application/test2+json', - body: () => schema.object({ ok: schema.literal(false) }), - }, - unsafe: { body: false }, - }, - }), - }, - }, - ], -}; let oasConverter: OasConverter; beforeEach(() => { oasConverter = new OasConverter(); @@ -81,7 +27,9 @@ beforeEach(() => { describe('extractVersionedRequestBodies', () => { test('handles full request config as expected', () => { - expect(extractVersionedRequestBodies(route, oasConverter, ['application/json'])).toEqual({ + expect( + extractVersionedRequestBodies(createTestRoute(), oasConverter, ['application/json']) + ).toEqual({ 'application/json; Elastic-Api-Version=2023-10-31': { schema: { additionalProperties: false, @@ -112,8 +60,11 @@ describe('extractVersionedRequestBodies', () => { describe('extractVersionedResponses', () => { test('handles full response config as expected', () => { - expect(extractVersionedResponses(route, oasConverter, ['application/test+json'])).toEqual({ + expect( + extractVersionedResponses(createTestRoute(), oasConverter, ['application/test+json']) + ).toEqual({ 200: { + description: 'OK response 2023-10-31\nOK response 2024-12-31', // merge multiple version descriptions content: { 'application/test+json; Elastic-Api-Version=2023-10-31': { schema: { @@ -138,6 +89,7 @@ describe('extractVersionedResponses', () => { }, }, 404: { + description: 'Not Found response 2023-10-31', content: { 'application/test2+json; Elastic-Api-Version=2023-10-31': { schema: { @@ -172,7 +124,7 @@ describe('extractVersionedResponses', () => { describe('processVersionedRouter', () => { it('correctly extracts the version based on the version filter', () => { const baseCase = processVersionedRouter( - { getRoutes: () => [route] } as unknown as CoreVersionedRouter, + { getRoutes: () => [createTestRoute()] } as unknown as CoreVersionedRouter, new OasConverter(), createOperationIdCounter(), {} @@ -184,7 +136,7 @@ describe('processVersionedRouter', () => { ]); const filteredCase = processVersionedRouter( - { getRoutes: () => [route] } as unknown as CoreVersionedRouter, + { getRoutes: () => [createTestRoute()] } as unknown as CoreVersionedRouter, new OasConverter(), createOperationIdCounter(), { version: '2023-10-31' } @@ -194,3 +146,61 @@ describe('processVersionedRouter', () => { ]); }); }); + +const createTestRoute: () => VersionedRouterRoute = () => ({ + path: '/foo', + method: 'get', + options: { + access: 'public', + options: { body: { access: ['application/test+json'] } as any }, + }, + handlers: [ + { + fn: jest.fn(), + options: { + version: '2023-10-31', + validate: () => ({ + request: { + body: schema.object({ foo: schema.string() }), + }, + response: { + 200: { + description: 'OK response 2023-10-31', + bodyContentType: 'application/test+json', + body: () => schema.object({ bar: schema.number({ min: 1, max: 99 }) }), + }, + 404: { + description: 'Not Found response 2023-10-31', + bodyContentType: 'application/test2+json', + body: () => schema.object({ ok: schema.literal(false) }), + }, + unsafe: { body: false }, + }, + }), + }, + }, + { + fn: jest.fn(), + options: { + version: '2024-12-31', + validate: () => ({ + request: { + body: schema.object({ foo2: schema.string() }), + }, + response: { + 200: { + description: 'OK response 2024-12-31', + bodyContentType: 'application/test+json', + body: () => schema.object({ bar2: schema.number({ min: 1, max: 99 }) }), + }, + 500: { + bodyContentType: 'application/test2+json', + body: () => schema.object({ ok: schema.literal(false) }), + }, + unsafe: { body: false }, + }, + }), + }, + }, + ], +}); diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts index 19b41f4812a30..38b8563be55af 100644 --- a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts +++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts @@ -15,6 +15,7 @@ import { import type { OpenAPIV3 } from 'openapi-types'; import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas'; import type { OasConverter } from './oas_converter'; +import { isReferenceObject } from './oas_converter/common'; import type { OperationIdCounter } from './operation_id_counter'; import { prepareRoutes, @@ -24,6 +25,7 @@ import { getVersionedHeaderParam, getVersionedContentTypeString, extractTags, + mergeResponseContent, } from './util'; export const processVersionedRouter = ( @@ -153,31 +155,49 @@ export const extractVersionedResponse = ( const result: OpenAPIV3.ResponsesObject = {}; const { unsafe, ...responses } = schemas.response; for (const [statusCode, responseSchema] of Object.entries(responses)) { - const maybeSchema = unwrapVersionedResponseBodyValidation(responseSchema.body); - const schema = converter.convert(maybeSchema); - const contentTypeString = getVersionedContentTypeString( - handler.options.version, - responseSchema.bodyContentType ? [responseSchema.bodyContentType] : contentType - ); - result[statusCode] = { - ...result[statusCode], - content: { - ...((result[statusCode] ?? {}) as OpenAPIV3.ResponseObject).content, + let newContent: OpenAPIV3.ResponseObject['content']; + if (responseSchema.body) { + const maybeSchema = unwrapVersionedResponseBodyValidation(responseSchema.body); + const schema = converter.convert(maybeSchema); + const contentTypeString = getVersionedContentTypeString( + handler.options.version, + responseSchema.bodyContentType ? [responseSchema.bodyContentType] : contentType + ); + newContent = { [contentTypeString]: { schema, }, - }, + }; + } + result[statusCode] = { + ...result[statusCode], + description: responseSchema.description!, + ...mergeResponseContent( + ((result[statusCode] ?? {}) as OpenAPIV3.ResponseObject).content, + newContent + ), }; } return result; }; +const mergeDescriptions = ( + existing: undefined | string, + toAppend: OpenAPIV3.ResponsesObject[string] +): string | undefined => { + if (!isReferenceObject(toAppend) && toAppend.description) { + return existing?.length ? `${existing}\n${toAppend.description}` : toAppend.description; + } + return existing; +}; + const mergeVersionedResponses = (a: OpenAPIV3.ResponsesObject, b: OpenAPIV3.ResponsesObject) => { const result: OpenAPIV3.ResponsesObject = Object.assign({}, a); for (const [statusCode, responseContent] of Object.entries(b)) { const existing = (result[statusCode] as OpenAPIV3.ResponseObject) ?? {}; result[statusCode] = { ...result[statusCode], + description: mergeDescriptions(existing.description, responseContent)!, content: Object.assign( {}, existing.content, diff --git a/packages/kbn-router-to-openapispec/src/util.test.ts b/packages/kbn-router-to-openapispec/src/util.test.ts index b4008249fed88..0b69ee9fbc6b2 100644 --- a/packages/kbn-router-to-openapispec/src/util.test.ts +++ b/packages/kbn-router-to-openapispec/src/util.test.ts @@ -7,7 +7,7 @@ */ import { OpenAPIV3 } from 'openapi-types'; -import { buildGlobalTags, prepareRoutes } from './util'; +import { buildGlobalTags, mergeResponseContent, prepareRoutes } from './util'; import { assignToPaths, extractTags } from './util'; describe('extractTags', () => { @@ -159,3 +159,29 @@ describe('prepareRoutes', () => { expect(prepareRoutes(input, filters)).toEqual(output); }); }); + +describe('mergeResponseContent', () => { + it('returns an empty object if no content is provided', () => { + expect(mergeResponseContent(undefined, undefined)).toEqual({}); + expect(mergeResponseContent({}, {})).toEqual({}); + }); + + it('merges content objects', () => { + expect( + mergeResponseContent( + { + ['application/json+v1']: { encoding: {} }, + }, + { + ['application/json+v1']: { example: 'overridden' }, + ['application/json+v2']: {}, + } + ) + ).toEqual({ + content: { + ['application/json+v1']: { example: 'overridden' }, + ['application/json+v2']: {}, + }, + }); + }); +}); diff --git a/packages/kbn-router-to-openapispec/src/util.ts b/packages/kbn-router-to-openapispec/src/util.ts index 315b1478d4504..786dcbd5fa120 100644 --- a/packages/kbn-router-to-openapispec/src/util.ts +++ b/packages/kbn-router-to-openapispec/src/util.ts @@ -131,3 +131,14 @@ export const assignToPaths = ( const pathName = path.replace('?', ''); paths[pathName] = { ...paths[pathName], ...pathObject }; }; + +export const mergeResponseContent = ( + a: OpenAPIV3.ResponseObject['content'], + b: OpenAPIV3.ResponseObject['content'] +) => { + const mergedContent = { + ...(a ?? {}), + ...(b ?? {}), + }; + return { ...(Object.keys(mergedContent).length ? { content: mergedContent } : {}) }; +}; diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts b/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts index 31afcac759357..57f819bb44771 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts @@ -62,13 +62,36 @@ export async function runCheckFtrConfigsCli() { return false; } - if (file.match(/jest.config.(t|j)s$/)) { + if (file.match(/(jest(\.integration)?)\.config\.(t|j)s$/)) { return false; } - return readFileSync(file) - .toString() - .match(/(testRunner)|(testFiles)/); + if (file.match(/mocks.ts$/)) { + return false; + } + + const fileContent = readFileSync(file).toString(); + + if (fileContent.match(/(testRunner)|(testFiles)/)) { + // test config + return true; + } + + if (fileContent.match(/(describe)|(defineCypressConfig)/)) { + // test file or Cypress config + return false; + } + + // FTR config file should have default export + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const exports = require(file); + const defaultExport = exports.__esModule ? exports.default : exports; + return !!defaultExport; + } catch (err) { + log.debug(`Failed to load file: ${err.message}`); + return false; + } }); const { allFtrConfigs, manifestPaths } = getAllFtrConfigsAndManifests(); @@ -77,10 +100,14 @@ export async function runCheckFtrConfigsCli() { if (invalid.length) { const invalidList = invalid.map((path) => Path.relative(REPO_ROOT, path)).join('\n - '); log.error( - `The following files look like FTR configs which are not listed in one of manifest files:\nstateful: ${manifestPaths.stateful}\nserverless: ${manifestPaths.serverless}\n - ${invalidList}` + `The following files look like FTR configs which are not listed in one of manifest files:\n${invalidList}\n +Make sure to add your new FTR config to the correct manifest file.\n +Stateful tests:\n${(manifestPaths.stateful as string[]).join('\n')}\n +Serverless tests:\n${(manifestPaths.serverless as string[]).join('\n')} + ` ); throw createFailError( - `Please add the listed paths to the correct manifest file. If it's not an FTR config, you can add it to the IGNORED_PATHS in ${THIS_REL} or contact #kibana-operations` + `Please add the listed paths to the correct manifest files. If it's not an FTR config, you can add it to the IGNORED_PATHS in ${THIS_REL} or contact #kibana-operations` ); } }, diff --git a/packages/kbn-test/src/kbn_client/kbn_client_requester.ts b/packages/kbn-test/src/kbn_client/kbn_client_requester.ts index 2c81f833888f6..8639f8e7507a3 100644 --- a/packages/kbn-test/src/kbn_client/kbn_client_requester.ts +++ b/packages/kbn-test/src/kbn_client/kbn_client_requester.ts @@ -121,7 +121,6 @@ export class KbnClientRequester { const maxAttempts = options.retries ?? DEFAULT_MAX_ATTEMPTS; const msgOrThrow = errMsg({ redacted, - attempt, maxAttempts, requestedRetries: options.retries !== undefined, failedToGetResponseSvc: (error: Error) => isAxiosRequestError(error), @@ -139,7 +138,10 @@ export class KbnClientRequester { await delay(1000 * attempt); continue; } - throw new KbnClientRequesterError(`${msgOrThrow(error)} -- and ran out of retries`, error); + throw new KbnClientRequesterError( + `${msgOrThrow(attempt, error)} -- and ran out of retries`, + error + ); } } } @@ -147,21 +149,19 @@ export class KbnClientRequester { export function errMsg({ redacted, - attempt, - maxAttempts, requestedRetries, + maxAttempts, failedToGetResponseSvc, path, method, description, }: ReqOptions & { redacted: string; - attempt: number; maxAttempts: number; requestedRetries: boolean; failedToGetResponseSvc: (x: Error) => boolean; }) { - return function errMsgOrReThrow(_: any) { + return function errMsgOrReThrow(attempt: number, _: any) { const result = isConcliftOnGetError(_) ? `Conflict on GET (path=${path}, attempt=${attempt}/${maxAttempts})` : requestedRetries || failedToGetResponseSvc(_) diff --git a/packages/kbn-try-in-console/components/try_in_console_button.tsx b/packages/kbn-try-in-console/components/try_in_console_button.tsx index a49011749e14e..e54b139fe6734 100644 --- a/packages/kbn-try-in-console/components/try_in_console_button.tsx +++ b/packages/kbn-try-in-console/components/try_in_console_button.tsx @@ -72,12 +72,27 @@ export const TryInConsoleButton = ({ ); } + const getAriaLabel = () => { + if ( + consolePlugin?.openEmbeddedConsole !== undefined && + consolePlugin?.isEmbeddedConsoleAvailable?.() + ) { + return i18n.translate('tryInConsole.embeddedConsoleButton', { + defaultMessage: 'Try the snipped in the Console - opens in embedded console', + }); + } + return i18n.translate('tryInConsole.inNewTab.button', { + defaultMessage: 'Try the below snippet in Console - opens in a new tab', + }); + }; + return ( {content ?? TRY_IN_CONSOLE} diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts index f422d9fae221a..052d7592024d7 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts +++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts @@ -28,6 +28,8 @@ export const CreateDebPackage: Task = { 'amd64', '--deb-priority', 'optional', + '--depends', + ' adduser', ]); await runFpm(config, log, build, 'deb', 'arm64', [ @@ -35,6 +37,8 @@ export const CreateDebPackage: Task = { 'arm64', '--deb-priority', 'optional', + '--depends', + ' adduser', ]); }, }; 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 a5c370c48255f..0b856b639a561 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 @@ -139,6 +139,7 @@ export const applicationUsageSchema = { enterpriseSearchAnalytics: commonSchema, enterpriseSearchApplications: commonSchema, enterpriseSearchAISearch: commonSchema, + enterpriseSearchSemanticSearch: commonSchema, enterpriseSearchVectorSearch: commonSchema, enterpriseSearchElasticsearch: commonSchema, appSearch: commonSchema, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index d71db1c3ab98f..805b290de5f76 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -2622,6 +2622,137 @@ } } }, + "enterpriseSearchSemanticSearch": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "Always `main`" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen over the last 90 days" + } + }, + "views": { + "type": "array", + "items": { + "properties": { + "appId": { + "type": "keyword", + "_meta": { + "description": "The application being tracked" + } + }, + "viewId": { + "type": "keyword", + "_meta": { + "description": "The application view being tracked" + } + }, + "clicks_total": { + "type": "long", + "_meta": { + "description": "General number of clicks in the application sub view since we started counting them" + } + }, + "clicks_7_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 7 days" + } + }, + "clicks_30_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 30 days" + } + }, + "clicks_90_days": { + "type": "long", + "_meta": { + "description": "General number of clicks in the active application sub view over the last 90 days" + } + }, + "minutes_on_screen_total": { + "type": "float", + "_meta": { + "description": "Minutes the application sub view is active and on-screen since we started counting them." + } + }, + "minutes_on_screen_7_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 7 days" + } + }, + "minutes_on_screen_30_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 30 days" + } + }, + "minutes_on_screen_90_days": { + "type": "float", + "_meta": { + "description": "Minutes the application is active and on-screen active application sub view over the last 90 days" + } + } + } + } + } + } + }, "enterpriseSearchVectorSearch": { "properties": { "appId": { diff --git a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts index 8e7adb504ebee..99a86bbe23791 100644 --- a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts +++ b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts @@ -21,7 +21,8 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); - describe('saved objects management with hidden types', () => { + // Failing: See https://github.com/elastic/kibana/issues/118488 + describe.skip('saved objects management with hidden types', () => { before(async () => { await esArchiver.load( 'test/functional/fixtures/es_archiver/saved_objects_management/hidden_types' diff --git a/x-pack/packages/kbn-entities-schema/index.ts b/x-pack/packages/kbn-entities-schema/index.ts index 92b93b7938125..8251e1c14755f 100644 --- a/x-pack/packages/kbn-entities-schema/index.ts +++ b/x-pack/packages/kbn-entities-schema/index.ts @@ -8,3 +8,6 @@ export * from './src/schema/entity_definition'; export * from './src/schema/entity'; export * from './src/schema/common'; +export * from './src/rest_spec/delete'; +export * from './src/rest_spec/reset'; +export * from './src/rest_spec/get'; diff --git a/x-pack/packages/kbn-entities-schema/src/rest_spec/delete.ts b/x-pack/packages/kbn-entities-schema/src/rest_spec/delete.ts new file mode 100644 index 0000000000000..b1243d5aa6d9e --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/rest_spec/delete.ts @@ -0,0 +1,16 @@ +/* + * 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'; + +export const deleteEntityDefinitionParamsSchema = z.object({ + id: z.string(), +}); + +export const deleteEntityDefinitionQuerySchema = z.object({ + deleteData: z.optional(z.coerce.boolean().default(false)), +}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details_route.ts b/x-pack/packages/kbn-entities-schema/src/rest_spec/get.ts similarity index 56% rename from x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details_route.ts rename to x-pack/packages/kbn-entities-schema/src/rest_spec/get.ts index fcc7dad089bab..f703da8a7b6b2 100644 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details_route.ts +++ b/x-pack/packages/kbn-entities-schema/src/rest_spec/get.ts @@ -5,10 +5,9 @@ * 2.0. */ -import * as t from 'io-ts'; +import { z } from 'zod'; -export const getAgentDetailsRequestParamsSchema = t.unknown; - -export type GetAgentDetailsRequestParamsSchema = t.OutputOf< - typeof getAgentDetailsRequestParamsSchema ->; +export const getEntityDefinitionQuerySchema = z.object({ + page: z.optional(z.coerce.number()), + perPage: z.optional(z.coerce.number()), +}); diff --git a/x-pack/packages/kbn-entities-schema/src/rest_spec/reset.ts b/x-pack/packages/kbn-entities-schema/src/rest_spec/reset.ts new file mode 100644 index 0000000000000..e93b8e789280f --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/rest_spec/reset.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { z } from 'zod'; + +export const resetEntityDefinitionParamsSchema = z.object({ + id: z.string(), +}); diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index f2be720d1c04c..1e498b92fd0ac 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -177,6 +177,23 @@ export const VECTOR_SEARCH_PLUGIN = { URL: '/app/enterprise_search/vector_search', }; +export const SEMANTIC_SEARCH_PLUGIN = { + DESCRIPTION: i18n.translate('xpack.enterpriseSearch.SemanticSearch.description', { + defaultMessage: + 'Easily add semantic search to Elasticsearch with inference endpoints and the semantic_text field type, to boost search relevance.', + }), + ID: 'enterpriseSearchSemanticSearch', + LOGO: 'logoEnterpriseSearch', + NAME: i18n.translate('xpack.enterpriseSearch.SemanticSearch.productName', { + defaultMessage: 'Semantic Search', + }), + NAV_TITLE: i18n.translate('xpack.enterpriseSearch.SemanticSearch.navTitle', { + defaultMessage: 'Semantic Search', + }), + SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/', + URL: '/app/enterprise_search/semantic_search', +}; + export const INFERENCE_ENDPOINTS_PLUGIN = { ID: ENTERPRISE_SEARCH_RELEVANCE_APP_ID, NAME: i18n.translate('xpack.enterpriseSearch.inferenceEndpoints.productName', { diff --git a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/ai_search_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/ai_search_guide.tsx index 67c014d572ac8..7374ecd0ac359 100644 --- a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/ai_search_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/ai_search_guide.tsx @@ -40,7 +40,7 @@ export const AISearchGuide: React.FC = () => { bottomBorder={false} pageHeader={{ pageTitle: i18n.translate('xpack.enterpriseSearch.aiSearch.guide.pageTitle', { - defaultMessage: 'Enhance your search with AI', + defaultMessage: 'Improve search revelance with AI', }), }} > @@ -81,11 +81,11 @@ export const AISearchGuide: React.FC = () => { - + - + diff --git a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/elser_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/elser_panel.tsx index c56ea6aa1b277..65003e362c3ed 100644 --- a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/elser_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/elser_panel.tsx @@ -7,125 +7,72 @@ import React from 'react'; -import { generatePath } from 'react-router-dom'; +import { useValues } from 'kea'; -import { - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiSpacer, - EuiSteps, - EuiText, -} from '@elastic/eui'; -import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants'; -import { NEW_INDEX_PATH } from '../../../enterprise_search_content/routes'; +import { SEMANTIC_SEARCH_PLUGIN } from '../../../../../common/constants'; import { docLinks } from '../../../shared/doc_links'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; +import { HttpLogic } from '../../../shared/http'; +import { KibanaLogic } from '../../../shared/kibana'; -const steps: EuiContainedStepProps[] = [ - { - title: i18n.translate('xpack.enterpriseSearch.aiSearch.elserPanel.step1.title', { - defaultMessage: 'Create an index', - }), - children: ( - - - {i18n.translate('xpack.enterpriseSearch.aiSearch.elserPanel.step1.buttonLabel', { - defaultMessage: 'Create an index', - })} - - - ), - status: 'incomplete', - }, - { - title: i18n.translate('xpack.enterpriseSearch.aiSearch.elserPanel.step2.title', { - defaultMessage: "Navigate to index's Pipelines tab", - }), - children: ( - -

- - " - {i18n.translate( - 'xpack.enterpriseSearch.aiSearch.elserPanel.step2.description.pipelinesName', - { - defaultMessage: 'Pipelines', - } - )} - " - - ), - }} - /> -

-
- ), - status: 'incomplete', - }, - { - title: i18n.translate('xpack.enterpriseSearch.aiSearch.elserPanel.step3.title', { - defaultMessage: 'Follow the on-screen instructions to deploy ELSER', - }), - children: ( - -

- -

-
- ), - status: 'incomplete', - }, -]; +export const ElserPanel: React.FC = () => { + const { http } = useValues(HttpLogic); + const { application } = useValues(KibanaLogic); -export const ElserPanel: React.FC = () => ( - <> - - - - -

- - {i18n.translate( - 'xpack.enterpriseSearch.aiSearch.elser.description.elserLinkText', - { - defaultMessage: 'Elastic Learned Sparse Encoder v2', - } - )} - - ), - }} - /> -

-
-
- - - -
- -); + return ( + <> + + + + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.aiSearch.elser.description.elserLinkText', + { + defaultMessage: 'Elastic Learned Sparse Encoder v2', + } + )} + + ), + }} + /> +

+
+
+ + { + application.navigateToUrl( + http.basePath.prepend(`${SEMANTIC_SEARCH_PLUGIN.URL}?model_example=elser`) + ); + }} + > + + {i18n.translate('xpack.enterpriseSearch.aiSearch.elserPanel.buttonLabel', { + defaultMessage: 'Set up Semantic Search', + })} + + + +
+ + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rank_aggregation_section.tsx b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rank_aggregation_section.tsx index b495df58d5435..64e39d1e58f34 100644 --- a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rank_aggregation_section.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rank_aggregation_section.tsx @@ -30,7 +30,7 @@ export const RankAggregationSection: React.FC = () => {

@@ -40,7 +40,7 @@ export const RankAggregationSection: React.FC = () => {

diff --git a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rrf_ranking_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rrf_ranking_panel.tsx index 8353a7de8a148..75eade2b2e07b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rrf_ranking_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/rrf_ranking_panel.tsx @@ -7,24 +7,12 @@ import React from 'react'; -import { generatePath } from 'react-router-dom'; - -import { - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiSpacer, - EuiSteps, - EuiText, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiSteps, EuiText } from '@elastic/eui'; import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DEV_TOOLS_CONSOLE_PATH } from '../../../enterprise_search_content/routes'; import { docLinks } from '../../../shared/doc_links'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; const steps: EuiContainedStepProps[] = [ { @@ -33,6 +21,7 @@ const steps: EuiContainedStepProps[] = [ }), children: ( - - {i18n.translate('xpack.enterpriseSearch.aiSearch.rrfRankingPanel.step2.buttonLabel', { - defaultMessage: 'Open Console', - })} - - + {i18n.translate('xpack.enterpriseSearch.aiSearch.rrfRankingPanel.step2.buttonLabel', { + defaultMessage: 'View Notebook', + })} + ), status: 'incomplete', }, @@ -79,7 +68,12 @@ export const RrfRankingPanel: React.FC = () => ( defaultMessage="Use {rrf} to combine rankings from multiple result sets with different relevance indicators, with no fine tuning required." values={{ rrf: ( - + {i18n.translate('xpack.enterpriseSearch.aiSearch.rrfRankingPanel.rrfLinkText', { defaultMessage: 'Reciprocal Rank Fusion (RRF)', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/semantic_search_section.tsx b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/semantic_search_section.tsx index 622e3144eeaee..14a61bfa3e3cf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/semantic_search_section.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/semantic_search_section.tsx @@ -32,7 +32,7 @@ export const SemanticSearchSection: React.FC = () => {

@@ -42,7 +42,7 @@ export const SemanticSearchSection: React.FC = () => {

@@ -58,7 +58,7 @@ export const SemanticSearchSection: React.FC = () => { initialIsOpen icon={elserIllustration} title={i18n.translate('xpack.enterpriseSearch.aiSearch.elserAccordion.title', { - defaultMessage: 'Elastic Learned Sparse Encoder', + defaultMessage: 'Semantic search with ELSER', })} description={i18n.translate( 'xpack.enterpriseSearch.aiSearch.elserAccordion.description', @@ -106,7 +106,7 @@ export const SemanticSearchSection: React.FC = () => { description={i18n.translate( 'xpack.enterpriseSearch.aiSearch.nlpEnrichmentAccordion.description', { - defaultMessage: 'Insightful data enrichment with trained ML models', + defaultMessage: 'Extract entities and sentiment from text', } )} currentExpandedId={currentExpandedId} diff --git a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/vector_search_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/vector_search_panel.tsx index f9adf365efadc..f28f1fca8839a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/vector_search_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/ai_search/components/ai_search_guide/vector_search_panel.tsx @@ -7,156 +7,71 @@ import React from 'react'; -import { generatePath } from 'react-router-dom'; +import { useValues } from 'kea'; -import { - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiSpacer, - EuiSteps, - EuiText, -} from '@elastic/eui'; -import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants'; -import { - ML_MANAGE_TRAINED_MODELS_PATH, - NEW_INDEX_PATH, -} from '../../../enterprise_search_content/routes'; +import { VECTOR_SEARCH_PLUGIN } from '../../../../../common/constants'; import { docLinks } from '../../../shared/doc_links'; -import { EuiLinkTo } from '../../../shared/react_router_helpers'; +import { HttpLogic } from '../../../shared/http'; +import { KibanaLogic } from '../../../shared/kibana'; -const steps: EuiContainedStepProps[] = [ - { - title: i18n.translate('xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.title', { - defaultMessage: 'Learn how to upload ML models', - }), - children: ( +export const VectorSearchPanel: React.FC = () => { + const { http } = useValues(HttpLogic); + const { application } = useValues(KibanaLogic); + + return ( + <> + - - - {i18n.translate( - 'xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.guideToTrainedModelsLinkText', - { defaultMessage: 'Guide to trained models' } - )} - + + +

+ + {i18n.translate( + 'xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description.vectorDbCapabilitiesLinkText', + { + defaultMessage: "Elasticsearch's vector DB capabilities", + } + )} + + ), + }} + /> +

+
- - + { + application.navigateToUrl(http.basePath.prepend(`${VECTOR_SEARCH_PLUGIN.URL}`)); + }} > - - {i18n.translate( - 'xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.buttonLabel', - { - defaultMessage: 'View trained models', - } - )} + + {i18n.translate('xpack.enterpriseSearch.aiSearch.vectorSearchPanel.buttonLabel', { + defaultMessage: 'Set up Vector Search', + })} - +
- ), - status: 'incomplete', - }, - { - title: i18n.translate('xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.title', { - defaultMessage: 'Create an index', - }), - children: ( - - - {i18n.translate('xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.buttonLabel', { - defaultMessage: 'Create an index', - })} - - - ), - status: 'incomplete', - }, - { - title: i18n.translate('xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.title', { - defaultMessage: 'Create a ML inference pipeline', - }), - children: ( - -

- - " - {i18n.translate( - 'xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.description.pipelinesName', - { - defaultMessage: 'Pipelines', - } - )} - " - - ), - }} - /> -

-
- ), - status: 'incomplete', - }, -]; - -export const VectorSearchPanel: React.FC = () => ( - <> - - - - -

- - {i18n.translate( - 'xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description.vectorDbCapabilitiesLinkText', - { - defaultMessage: "Elasticsearch's vector DB capabilities", - } - )} - - ), - }} - /> -

-
-
- - - -
- -); + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/layout/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/layout/page_template.tsx new file mode 100644 index 0000000000000..72ddbffc263c7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/layout/page_template.tsx @@ -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 React from 'react'; + +import { SEARCH_PRODUCT_NAME } from '../../../../../common/constants'; +import { SetSemanticSearchChrome } from '../../../shared/kibana_chrome'; +import { + EnterpriseSearchPageTemplateWrapper, + PageTemplateProps, + useEnterpriseSearchNav, +} from '../../../shared/layout'; +import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry'; + +export const EnterpriseSearchSemanticSearchPageTemplate: React.FC = ({ + children, + pageChrome, + pageViewTelemetry, + ...pageTemplateProps +}) => ( + } + > + {pageViewTelemetry && ( + + )} + {children} + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/semantic_search_guide/semantic_search_guide.scss b/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/semantic_search_guide/semantic_search_guide.scss new file mode 100644 index 0000000000000..15fb1b242ac76 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/semantic_search_guide/semantic_search_guide.scss @@ -0,0 +1,6 @@ +.chooseEmbeddingModelSelectedBorder { + border: 1px solid $euiColorPrimary; +} +.chooseEmbeddingModelBorder { + border: 1px solid $euiColorLightShade; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/semantic_search_guide/semantic_search_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/semantic_search_guide/semantic_search_guide.tsx new file mode 100644 index 0000000000000..1b373da7a9ff2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/semantic_search/components/semantic_search_guide/semantic_search_guide.tsx @@ -0,0 +1,360 @@ +/* + * 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 { useSearchParams } from 'react-router-dom-v5-compat'; + +import { + EuiCard, + EuiCode, + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiLink, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { SetAISearchChromeSearchDocsSection } from '../../../ai_search/components/ai_search_guide/ai_search_docs_section'; +import { docLinks } from '../../../shared/doc_links'; +import { SetSemanticSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { DevToolsConsoleCodeBlock } from '../../../vector_search/components/dev_tools_console_code_block/dev_tools_console_code_block'; +import './semantic_search_guide.scss'; +import { EnterpriseSearchSemanticSearchPageTemplate } from '../layout/page_template'; + +const SETUP_INFERENCE_ENDPOINT_ELSER = `PUT _inference/sparse_embedding/my-inference-endpoint +{ + "service": "elser", + "service_settings": { + "num_allocations": 1, + "num_threads": 1 + } +} +`; + +const SETUP_INFERENCE_ENDPOINT_E5 = `PUT _inference/text_embedding/my-inference-endpoint +{ + "service": "elasticsearch", + "service_settings": { + "model_id": ".multilingual-e5-small", + "num_allocations": 1, + "num_threads": 1 + } +} +`; + +const SETUP_INFERENCE_ENDPOINT_OPENAI = `PUT _inference/text_embedding/my-inference-endpoint +{ + "service": "openai", + "service_settings": { + "model_id": "text-embedding-3-small", + "api_key": "", + } +} +`; + +const SETUP_INFERENCE_ENDPOINT_BEDROCK = `PUT _inference/text_embedding/my-inference-endpoint +{ + "service": "amazonbedrock", + "service_settings": { + "access_key": "", + "secret_key": "", + "region": "us-east-1", + "provider": "amazontitan", + "model": "amazon.titan-embed-text-v2:0" + } +} +`; + +const CREATE_INDEX_SNIPPET = `PUT /my-index +{ + "mappings": { + "properties": { + "text": { + "type": "semantic_text", + "inference_id": "my-inference-endpoint" + } + } + } +}`; + +const INGEST_SNIPPET = `POST /my-index/_doc +{ + "text": "There are a few foods and food groups that will help to fight inflammation and delayed onset muscle soreness (both things that are inevitable after a long, hard workout) when you incorporate them into your postworkout eats, whether immediately after your run or at a meal later in the day" +}`; + +const QUERY_SNIPPET = `POST /my-index/_search +{ + "size" : 3, + "query" : { + "semantic": { + "field": "text", + "query": "How to avoid muscle soreness while running?" + } + } +}`; + +const modelSelection: InferenceModel[] = [ + { + id: 'elser', + modelName: 'ELSER', + code: SETUP_INFERENCE_ENDPOINT_ELSER, + link: docLinks.elser, + description: "Elastic's proprietary, best-in-class sparse vector model for semantic search.", + }, + { + id: 'e5', + modelName: 'E5 Multilingual', + code: SETUP_INFERENCE_ENDPOINT_E5, + link: docLinks.e5Model, + description: 'Try an optimized third party multilingual model.', + }, + { + code: SETUP_INFERENCE_ENDPOINT_OPENAI, + id: 'openai', + modelName: 'OpenAI', + link: 'https://platform.openai.com/docs/guides/embeddings', + description: "Connect with OpenAI's embedding models.", + }, + { + id: 'bedrock', + modelName: 'Amazon Bedrock', + code: SETUP_INFERENCE_ENDPOINT_BEDROCK, + link: 'https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html', + description: "Use Amazon Bedrock's embedding models.", + }, +]; + +interface SelectModelPanelProps { + isSelectedModel: boolean; + model: InferenceModel; + setSelectedModel: (model: InferenceModel) => void; +} + +interface InferenceModel { + code: string; + id: string; + link: string; + modelName: string; + description: string; +} + +const SelectModelPanel: React.FC = ({ + model, + setSelectedModel, + isSelectedModel, +}) => { + return ( + + + +

{model.description}

+
+ + + + + + } + display={isSelectedModel ? 'primary' : 'plain'} + onClick={() => setSelectedModel(model)} + titleSize="xs" + hasBorder + textAlign="left" + /> +
+ ); +}; + +export const SemanticSearchGuide: React.FC = () => { + const [searchParams] = useSearchParams(); + const chosenUrlModel = + modelSelection.find((model) => model.id === searchParams.get('model_example')) || + modelSelection[0]; + const [selectedModel, setSelectedModel] = React.useState(chosenUrlModel); + + return ( + + {' '} + + + +

+ ), + pageTitle: ( + + ), + }} + > + + + + +

+ +

+
+ + +

+ +

+

+ + + +

+
+ + + {modelSelection.map((model) => ( + + ))} + +
+ + {selectedModel.code} + +
+ + + + +

+ +

+
+ + +

+ semantic_text }} + /> +

+
+
+ + {CREATE_INDEX_SNIPPET} + +
+ + + + +

+ +

+
+ + +

+ +

+
+
+ + {INGEST_SNIPPET} + +
+ + + + +

+ +

+
+ + +

+ +

+
+
+ + {QUERY_SNIPPET} + +
+ + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/semantic_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/semantic_search/index.tsx new file mode 100644 index 0000000000000..f33142bae940c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/semantic_search/index.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 React from 'react'; +// switch is not available on shared-ux-router +// eslint-disable-next-line no-restricted-imports +import { Switch } from 'react-router-dom'; + +import { Route } from '@kbn/shared-ux-router'; + +import { isVersionMismatch } from '../../../common/is_version_mismatch'; +import { InitialAppData } from '../../../common/types'; +import { VersionMismatchPage } from '../shared/version_mismatch'; + +import { SemanticSearchGuide } from './components/semantic_search_guide/semantic_search_guide'; + +import { ROOT_PATH } from './routes'; + +export const EnterpriseSearchSemanticSearch: React.FC = (props) => { + const { enterpriseSearchVersion, kibanaVersion } = props; + const incompatibleVersions = isVersionMismatch(enterpriseSearchVersion, kibanaVersion); + + return ( + + + {incompatibleVersions ? ( + + ) : ( + + )} + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/semantic_search/jest.config.js b/x-pack/plugins/enterprise_search/public/applications/semantic_search/jest.config.js new file mode 100644 index 0000000000000..7711d9b97523f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/semantic_search/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../../..', + roots: ['/x-pack/plugins/enterprise_search/public/applications/semantic_search'], + collectCoverage: true, + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/enterprise_search/public/applications/**/*.{ts,tsx}', + '!/x-pack/plugins/enterprise_search/public/*.ts', + '!/x-pack/plugins/enterprise_search/server/*.ts', + '!/x-pack/plugins/enterprise_search/public/applications/test_helpers/**/*.{ts,tsx}', + ], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/enterprise_search/public/applications/semantic_search', + modulePathIgnorePatterns: [ + '/x-pack/plugins/enterprise_search/public/applications/app_search/cypress', + '/x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress', + ], +}; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/index.ts b/x-pack/plugins/enterprise_search/public/applications/semantic_search/routes.ts similarity index 87% rename from x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/index.ts rename to x-pack/plugins/enterprise_search/public/applications/semantic_search/routes.ts index a112f6ca1b29f..d6b0b0a669281 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/semantic_search/routes.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './common_attributes'; +export const ROOT_PATH = '/'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts index 228321ef120c1..311043a442bc8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts @@ -103,6 +103,7 @@ class DocLinks { public crawlerOverview: string; public deployTrainedModels: string; public documentLevelSecurity: string; + public e5Model: string; public elasticsearchCreateIndex: string; public elasticsearchGettingStarted: string; public elasticsearchMapping: string; @@ -114,6 +115,7 @@ class DocLinks { public enterpriseSearchTroubleshootSetup: string; public enterpriseSearchUsersAccess: string; public indexApi: string; + public inferenceApiCreate: string; public ingestionApis: string; public ingestPipelines: string; public kibanaSecurity: string; @@ -138,6 +140,7 @@ class DocLinks { public searchTemplates: string; public searchUIAppSearch: string; public searchUIElasticsearch: string; + public semanticTextField: string; public start: string; public supportedNlpModels: string; public syncRules: string; @@ -280,6 +283,7 @@ class DocLinks { this.crawlerOverview = ''; this.deployTrainedModels = ''; this.documentLevelSecurity = ''; + this.e5Model = ''; this.elasticsearchCreateIndex = ''; this.elasticsearchGettingStarted = ''; this.elasticsearchMapping = ''; @@ -291,6 +295,7 @@ class DocLinks { this.enterpriseSearchTroubleshootSetup = ''; this.enterpriseSearchUsersAccess = ''; this.indexApi = ''; + this.inferenceApiCreate = ''; this.ingestionApis = ''; this.ingestPipelines = ''; this.kibanaSecurity = ''; @@ -315,6 +320,7 @@ class DocLinks { this.searchLabs = ''; this.searchLabsRepo = ''; this.searchTemplates = ''; + this.semanticTextField = ''; this.start = ''; this.supportedNlpModels = ''; this.syncRules = ''; @@ -459,6 +465,7 @@ class DocLinks { this.crawlerOverview = docLinks.links.enterpriseSearch.crawlerOverview; this.deployTrainedModels = docLinks.links.enterpriseSearch.deployTrainedModels; this.documentLevelSecurity = docLinks.links.enterpriseSearch.documentLevelSecurity; + this.e5Model = docLinks.links.enterpriseSearch.e5Model; this.elasticsearchCreateIndex = docLinks.links.elasticsearch.createIndex; this.elasticsearchGettingStarted = docLinks.links.elasticsearch.gettingStarted; this.elasticsearchMapping = docLinks.links.elasticsearch.mapping; @@ -470,6 +477,7 @@ class DocLinks { this.enterpriseSearchTroubleshootSetup = docLinks.links.enterpriseSearch.troubleshootSetup; this.enterpriseSearchUsersAccess = docLinks.links.enterpriseSearch.usersAccess; this.indexApi = docLinks.links.enterpriseSearch.indexApi; + this.inferenceApiCreate = docLinks.links.enterpriseSearch.inferenceApiCreate; this.ingestionApis = docLinks.links.enterpriseSearch.ingestionApis; this.ingestPipelines = docLinks.links.enterpriseSearch.ingestPipelines; this.kibanaSecurity = docLinks.links.kibana.xpackSecurity; @@ -494,6 +502,7 @@ class DocLinks { this.searchLabs = docLinks.links.enterpriseSearch.searchLabs; this.searchLabsRepo = docLinks.links.enterpriseSearch.searchLabsRepo; this.searchTemplates = docLinks.links.enterpriseSearch.searchTemplates; + this.semanticTextField = docLinks.links.enterpriseSearch.semanticTextField; this.start = docLinks.links.enterpriseSearch.start; this.supportedNlpModels = docLinks.links.enterpriseSearch.supportedNlpModels; this.syncRules = docLinks.links.enterpriseSearch.syncRules; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts index 5798a48680d1f..40e2d2fb27476 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts @@ -21,6 +21,7 @@ import { SEARCH_PRODUCT_NAME, VECTOR_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, + SEMANTIC_SEARCH_PLUGIN, } from '../../../../common/constants'; import { stripLeadingSlash } from '../../../../common/strip_slashes'; @@ -167,3 +168,6 @@ export const useAiSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => export const useVectorSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => useSearchBreadcrumbs([{ text: VECTOR_SEARCH_PLUGIN.NAV_TITLE, path: '/' }, ...breadcrumbs]); + +export const useSemanticSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => + useSearchBreadcrumbs([{ text: SEMANTIC_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts index c9400393b057b..eaeb30f1540d0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_title.ts @@ -12,6 +12,7 @@ import { ENTERPRISE_SEARCH_CONTENT_PLUGIN, SEARCH_EXPERIENCES_PLUGIN, SEARCH_PRODUCT_NAME, + SEMANTIC_SEARCH_PLUGIN, VECTOR_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, } from '../../../../common/constants'; @@ -55,5 +56,8 @@ export const aiSearchTitle = (page: Title = []) => generateTitle([...page, AI_SE export const vectorSearchTitle = (page: Title = []) => generateTitle([...page, VECTOR_SEARCH_PLUGIN.NAME]); +export const semanticSearchTitle = (page: Title = []) => + generateTitle([...page, SEMANTIC_SEARCH_PLUGIN.NAME]); + export const enterpriseSearchContentTitle = (page: Title = []) => generateTitle([...page, ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts index ae2151287b1d1..f9a6564ab5f28 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts @@ -16,5 +16,6 @@ export { SetWorkplaceSearchChrome, SetSearchExperiencesChrome, SetEnterpriseSearchApplicationsChrome, + SetSemanticSearchChrome, SetVectorSearchChrome, } from './set_chrome'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx index ac85bd89b6ba9..8f7c71d1309c0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx @@ -27,6 +27,7 @@ import { BreadcrumbTrail, useSearchExperiencesBreadcrumbs, useVectorSearchBreadcrumbs, + useSemanticSearchBreadcrumbs, } from './generate_breadcrumbs'; import { aiSearchTitle, @@ -36,6 +37,7 @@ import { enterpriseSearchContentTitle, searchExperiencesTitle, searchTitle, + semanticSearchTitle, vectorSearchTitle, workplaceSearchTitle, } from './generate_title'; @@ -242,5 +244,21 @@ export const SetVectorSearchChrome: React.FC = ({ trail = [] }) return null; }; +export const SetSemanticSearchChrome: React.FC = ({ trail = [] }) => { + const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic); + + const title = reverseArray(trail); + const docTitle = semanticSearchTitle(title); + + const breadcrumbs = useSemanticSearchBreadcrumbs(useGenerateBreadcrumbs(trail)); + + useEffect(() => { + setBreadcrumbs(breadcrumbs); + setDocTitle(docTitle); + }, [trail]); + + return null; +}; + // Small util - performantly reverses an array without mutating the original array const reverseArray = (array: string[]) => array.slice().reverse(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx index f818dfb9141f3..b2c31ff4868bc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx @@ -111,6 +111,12 @@ const baseNavItems = [ items: undefined, name: 'Vector Search', }, + { + href: '/app/enterprise_search/semantic_search', + id: 'semanticSearch', + items: undefined, + name: 'Semantic Search', + }, { href: '/app/enterprise_search/ai_search', id: 'aiSearch', 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 e39f0f0b71f29..d0f8a60f3472a 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 @@ -24,6 +24,7 @@ import { VECTOR_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, INFERENCE_ENDPOINTS_PLUGIN, + SEMANTIC_SEARCH_PLUGIN, } from '../../../../common/constants'; import { SEARCH_APPLICATIONS_PATH, @@ -204,6 +205,14 @@ export const useEnterpriseSearchNav = (alwaysReturn = false) => { to: VECTOR_SEARCH_PLUGIN.URL, }), }, + { + id: 'semanticSearch', + name: SEMANTIC_SEARCH_PLUGIN.NAME, + ...generateNavLink({ + shouldNotCreateHref: true, + to: SEMANTIC_SEARCH_PLUGIN.URL, + }), + }, { id: 'aiSearch', name: i18n.translate('xpack.enterpriseSearch.nav.aiSearchTitle', { diff --git a/x-pack/plugins/enterprise_search/public/applications/vector_search/components/vector_search_guide/vector_search_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/vector_search/components/vector_search_guide/vector_search_guide.tsx index 3ee0453067747..0cf9cb753c26a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/vector_search/components/vector_search_guide/vector_search_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/vector_search/components/vector_search_guide/vector_search_guide.tsx @@ -17,66 +17,56 @@ import { EuiHorizontalRule, EuiIcon, EuiLink, + EuiSpacer, EuiText, EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { AI_SEARCH_PLUGIN } from '../../../../../common/constants'; +import { SEMANTIC_SEARCH_PLUGIN } from '../../../../../common/constants'; import elserIllustration from '../../../../assets/images/elser.svg'; import nlpIllustration from '../../../../assets/images/nlp.svg'; import { docLinks } from '../../../shared/doc_links'; +import { HttpLogic } from '../../../shared/http'; import { KibanaLogic } from '../../../shared/kibana'; import { SetVectorSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { DevToolsConsoleCodeBlock } from '../dev_tools_console_code_block/dev_tools_console_code_block'; import { EnterpriseSearchVectorSearchPageTemplate } from '../layout/page_template'; -const CREATE_INDEX_SNIPPET = `PUT /image-index +const CREATE_INDEX_SNIPPET = `PUT /my-index { "mappings": { "properties": { - "image-vector": { + "vector": { "type": "dense_vector", - "dims": 3, - "index": true, - "similarity": "l2_norm" + "dims": 3 }, - "title-vector": { - "type": "dense_vector", - "dims": 5, - "index": true, - "similarity": "l2_norm" - }, - "title": { + "text": { "type": "text" - }, - "file-type": { - "type": "keyword" } } } }`; -const INGEST_SNIPPET = `POST /image-index/_bulk?refresh=true -{ "index": { "_id": "1" } } -{ "image-vector": [1, 5, -20], "title-vector": [12, 50, -10, 0, 1], "title": "moose family", "file-type": "jpg" } -{ "index": { "_id": "2" } } -{ "image-vector": [42, 8, -15], "title-vector": [25, 1, 4, -12, 2], "title": "alpine lake", "file-type": "png" } -{ "index": { "_id": "3" } } -{ "image-vector": [15, 11, 23], "title-vector": [1, 5, 25, 50, 20], "title": "full moon", "file-type": "jpg" }`; +const INGEST_SNIPPET = `POST /my-index/_doc +{ + "vector": [1, 5, -20], + "text": "hello world" +}`; -const QUERY_SNIPPET = `POST /image-index/_search +const QUERY_SNIPPET = `POST /my-index/_search { - "knn": { - "field": "image-vector", - "query_vector": [-5, 9, -12], - "k": 10, - "num_candidates": 100 - }, - "fields": [ "title", "file-type" ] + "size" : 3, + "query" : { + "knn": { + "field": "vector", + "query_vector": [1, 5, -20] + } + } }`; export const VectorSearchGuide: React.FC = () => { + const { http } = useValues(HttpLogic); const { application } = useValues(KibanaLogic); return ( @@ -120,6 +110,7 @@ export const VectorSearchGuide: React.FC = () => { /> +

{

@@ -165,7 +156,7 @@ export const VectorSearchGuide: React.FC = () => {

@@ -189,7 +180,7 @@ export const VectorSearchGuide: React.FC = () => {

@@ -197,7 +188,7 @@ export const VectorSearchGuide: React.FC = () => {

@@ -205,16 +196,18 @@ export const VectorSearchGuide: React.FC = () => { - application.navigateToApp(AI_SEARCH_PLUGIN.URL.replace(/^(?:\/app\/)?(.*)$/, '$1')) - } + onClick={() => { + application.navigateToUrl( + http.basePath.prepend(`${SEMANTIC_SEARCH_PLUGIN.URL}?model_example=elser`) + ); + }} layout="horizontal" titleSize="s" icon={} title={ } description={ @@ -224,22 +217,26 @@ export const VectorSearchGuide: React.FC = () => { /> } /> + { + application.navigateToUrl( + http.basePath.prepend(`${SEMANTIC_SEARCH_PLUGIN.URL}?model_example=e5`) + ); + }} layout="horizontal" titleSize="s" icon={} title={ } description={ } /> diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index 552bb43fbd073..496ce1821c0d1 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -55,6 +55,7 @@ import { VECTOR_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, INFERENCE_ENDPOINTS_PLUGIN, + SEMANTIC_SEARCH_PLUGIN, } from '../common/constants'; import { CreatIndexLocatorDefinition, @@ -383,6 +384,27 @@ export class EnterpriseSearchPlugin implements Plugin { title: VECTOR_SEARCH_PLUGIN.NAV_TITLE, }); + core.application.register({ + appRoute: SEMANTIC_SEARCH_PLUGIN.URL, + category: DEFAULT_APP_CATEGORIES.enterpriseSearch, + euiIconType: SEMANTIC_SEARCH_PLUGIN.LOGO, + id: SEMANTIC_SEARCH_PLUGIN.ID, + mount: async (params: AppMountParameters) => { + const kibanaDeps = await this.getKibanaDeps(core, params, cloud); + const { chrome, http } = kibanaDeps.core; + chrome.docTitle.change(SEMANTIC_SEARCH_PLUGIN.NAME); + + this.getInitialData(http); + const pluginData = this.getPluginData(); + + const { renderApp } = await import('./applications'); + const { EnterpriseSearchSemanticSearch } = await import('./applications/semantic_search'); + + return renderApp(EnterpriseSearchSemanticSearch, kibanaDeps, pluginData); + }, + title: SEMANTIC_SEARCH_PLUGIN.NAV_TITLE, + }); + core.application.register({ appRoute: AI_SEARCH_PLUGIN.URL, category: DEFAULT_APP_CATEGORIES.enterpriseSearch, diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index d5c917443654f..a03429729bf2f 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -229,6 +229,7 @@ export class EnterpriseSearchPlugin implements Plugin { enterpriseSearchApplications: showEnterpriseSearch, enterpriseSearchAISearch: showEnterpriseSearch, enterpriseSearchVectorSearch: showEnterpriseSearch, + enterpriseSearchSemanticSearch: showEnterpriseSearch, enterpriseSearchElasticsearch: showEnterpriseSearch, appSearch: hasAppSearchAccess && config.canDeployEntSearch, workplaceSearch: hasWorkplaceSearchAccess && config.canDeployEntSearch, @@ -241,6 +242,7 @@ export class EnterpriseSearchPlugin implements Plugin { enterpriseSearchApplications: showEnterpriseSearch, enterpriseSearchAISearch: showEnterpriseSearch, enterpriseSearchVectorSearch: showEnterpriseSearch, + enterpriseSearchSemanticSearch: showEnterpriseSearch, enterpriseSearchElasticsearch: showEnterpriseSearch, appSearch: hasAppSearchAccess && config.canDeployEntSearch, workplaceSearch: hasWorkplaceSearchAccess && config.canDeployEntSearch, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx index 197d64810c199..35fd048cc13cd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx @@ -149,7 +149,7 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ description: agent.last_checkin_message ? agent.last_checkin_message : '-', }, { - title: i18n.translate('xpack.fleet.agentDetails.hostIdLabel', { + title: i18n.translate('xpack.fleet.agentDetails.agentIdLabel', { defaultMessage: 'Agent ID', }), description: agent.id, @@ -197,6 +197,15 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ ? agent.local_metadata.host.hostname : '-', }, + { + title: i18n.translate('xpack.fleet.agentDetails.hostIdLabel', { + defaultMessage: 'Host ID', + }), + description: + typeof agent.local_metadata?.host?.id === 'string' + ? agent.local_metadata.host.id + : '-', + }, { title: i18n.translate('xpack.fleet.agentDetails.logLevel', { defaultMessage: 'Logging level', 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 a239b12deb5be..0b34d4453b2f1 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 @@ -249,9 +249,9 @@ describe('dimension editor', () => { userEvent.type(customPrefixTextbox, prefix); }; return { - settingNone: screen.getByTitle(/none/i), - settingAuto: screen.getByTitle(/auto/i), - settingCustom: screen.getByTitle(/custom/i), + settingNone: () => screen.getByTitle(/none/i), + settingAuto: () => screen.getByTitle(/auto/i), + settingCustom: () => screen.getByTitle(/custom/i), customPrefixTextbox, typePrefix, ...rtlRender, @@ -266,6 +266,11 @@ describe('dimension editor', () => { expect(screen.queryByTestId(SELECTORS.BREAKDOWN_EDITOR)).not.toBeInTheDocument(); }); + it(`doesn't break when layer data is missing`, () => { + renderSecondaryMetricEditor({ frame: { activeData: { first: undefined } } }); + expect(screen.getByTestId(SELECTORS.SECONDARY_METRIC_EDITOR)).toBeInTheDocument(); + }); + describe('metric prefix', () => { const NONE_PREFIX = ''; const AUTO_PREFIX = undefined; @@ -280,9 +285,9 @@ describe('dimension editor', () => { state: localState, }); - expect(settingAuto).toHaveAttribute('aria-pressed', 'true'); - expect(settingNone).toHaveAttribute('aria-pressed', 'false'); - expect(settingCustom).toHaveAttribute('aria-pressed', 'false'); + expect(settingAuto()).toHaveAttribute('aria-pressed', 'true'); + expect(settingNone()).toHaveAttribute('aria-pressed', 'false'); + expect(settingCustom()).toHaveAttribute('aria-pressed', 'false'); expect(customPrefixTextbox).not.toBeInTheDocument(); }); @@ -290,9 +295,9 @@ describe('dimension editor', () => { const { settingAuto, settingCustom, settingNone, customPrefixTextbox } = renderSecondaryMetricEditor({ state: { ...localState, secondaryPrefix: NONE_PREFIX } }); - expect(settingNone).toHaveAttribute('aria-pressed', 'true'); - expect(settingAuto).toHaveAttribute('aria-pressed', 'false'); - expect(settingCustom).toHaveAttribute('aria-pressed', 'false'); + expect(settingNone()).toHaveAttribute('aria-pressed', 'true'); + expect(settingAuto()).toHaveAttribute('aria-pressed', 'false'); + expect(settingCustom()).toHaveAttribute('aria-pressed', 'false'); expect(customPrefixTextbox).not.toBeInTheDocument(); }); @@ -301,9 +306,9 @@ describe('dimension editor', () => { const { settingAuto, settingCustom, settingNone, customPrefixTextbox } = renderSecondaryMetricEditor({ state: customPrefixState }); - expect(settingAuto).toHaveAttribute('aria-pressed', 'false'); - expect(settingNone).toHaveAttribute('aria-pressed', 'false'); - expect(settingCustom).toHaveAttribute('aria-pressed', 'true'); + expect(settingAuto()).toHaveAttribute('aria-pressed', 'false'); + expect(settingNone()).toHaveAttribute('aria-pressed', 'false'); + expect(settingCustom()).toHaveAttribute('aria-pressed', 'true'); expect(customPrefixTextbox).toHaveValue(customPrefixState.secondaryPrefix); }); @@ -316,12 +321,12 @@ describe('dimension editor', () => { state: { ...localState, secondaryPrefix: customPrefix }, }); - userEvent.click(settingNone); + userEvent.click(settingNone()); expect(setState).toHaveBeenCalledWith( expect.objectContaining({ secondaryPrefix: NONE_PREFIX }) ); - userEvent.click(settingAuto); + userEvent.click(settingAuto()); expect(setState).toHaveBeenCalledWith( expect.objectContaining({ secondaryPrefix: AUTO_PREFIX }) ); 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 f040c6dc86fa4..24248621c0982 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx @@ -131,7 +131,7 @@ function MaximumEditor({ setState, state, idPrefix }: SubProps) { } function SecondaryMetricEditor({ accessor, idPrefix, frame, layerId, setState, state }: SubProps) { - const columnName = getColumnByAccessor(accessor, frame.activeData?.[layerId].columns)?.name; + const columnName = getColumnByAccessor(accessor, frame.activeData?.[layerId]?.columns)?.name; const defaultPrefix = columnName || ''; return ( diff --git a/x-pack/plugins/observability_solution/entity_manager/common/constants_entities.ts b/x-pack/plugins/observability_solution/entity_manager/common/constants_entities.ts index 28e9823c15620..633dfa2f9fd29 100644 --- a/x-pack/plugins/observability_solution/entity_manager/common/constants_entities.ts +++ b/x-pack/plugins/observability_solution/entity_manager/common/constants_entities.ts @@ -18,8 +18,6 @@ export const ENTITY_EVENT_COMPONENT_TEMPLATE_V1 = // History constants export const ENTITY_HISTORY = 'history' as const; -export const ENTITY_HISTORY_INDEX_TEMPLATE_V1 = - `${ENTITY_BASE_PREFIX}_${ENTITY_SCHEMA_VERSION_V1}_${ENTITY_HISTORY}_index_template` as const; export const ENTITY_HISTORY_BASE_COMPONENT_TEMPLATE_V1 = `${ENTITY_BASE_PREFIX}_${ENTITY_SCHEMA_VERSION_V1}_${ENTITY_HISTORY}_base` as const; export const ENTITY_HISTORY_PREFIX_V1 = @@ -29,8 +27,6 @@ export const ENTITY_HISTORY_INDEX_PREFIX_V1 = // Latest constants export const ENTITY_LATEST = 'latest' as const; -export const ENTITY_LATEST_INDEX_TEMPLATE_V1 = - `${ENTITY_BASE_PREFIX}_${ENTITY_SCHEMA_VERSION_V1}_${ENTITY_LATEST}_index_template` as const; export const ENTITY_LATEST_BASE_COMPONENT_TEMPLATE_V1 = `${ENTITY_BASE_PREFIX}_${ENTITY_SCHEMA_VERSION_V1}_${ENTITY_LATEST}_base` as const; export const ENTITY_LATEST_PREFIX_V1 = diff --git a/x-pack/plugins/observability_solution/entity_manager/common/helpers.test.ts b/x-pack/plugins/observability_solution/entity_manager/common/helpers.test.ts new file mode 100644 index 0000000000000..50ce0caeba0a3 --- /dev/null +++ b/x-pack/plugins/observability_solution/entity_manager/common/helpers.test.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 { getEntityHistoryIndexTemplateV1, getEntityLatestIndexTemplateV1 } from './helpers'; + +describe('helpers', () => { + it('getEntityHistoryIndexTemplateV1 should return the correct value', () => { + const definitionId = 'test'; + const result = getEntityHistoryIndexTemplateV1(definitionId); + expect(result).toEqual('entities_v1_history_test_index_template'); + }); + + it('getEntityLatestIndexTemplateV1 should return the correct value', () => { + const definitionId = 'test'; + const result = getEntityLatestIndexTemplateV1(definitionId); + expect(result).toEqual('entities_v1_latest_test_index_template'); + }); +}); diff --git a/x-pack/plugins/observability_solution/entity_manager/common/helpers.ts b/x-pack/plugins/observability_solution/entity_manager/common/helpers.ts new file mode 100644 index 0000000000000..97a6317fee283 --- /dev/null +++ b/x-pack/plugins/observability_solution/entity_manager/common/helpers.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 { + ENTITY_BASE_PREFIX, + ENTITY_HISTORY, + ENTITY_LATEST, + ENTITY_SCHEMA_VERSION_V1, +} from './constants_entities'; + +export const getEntityHistoryIndexTemplateV1 = (definitionId: string) => + `${ENTITY_BASE_PREFIX}_${ENTITY_SCHEMA_VERSION_V1}_${ENTITY_HISTORY}_${definitionId}_index_template` as const; + +export const getEntityLatestIndexTemplateV1 = (definitionId: string) => + `${ENTITY_BASE_PREFIX}_${ENTITY_SCHEMA_VERSION_V1}_${ENTITY_LATEST}_${definitionId}_index_template` as const; diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/auth/privileges.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/auth/privileges.ts index 00f09209fb3b6..3bc88127a5964 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/auth/privileges.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/auth/privileges.ts @@ -21,7 +21,13 @@ export const requiredRunTimePrivileges = { privileges: ['read', 'view_index_metadata'], }, ], - cluster: ['manage_transform', 'monitor_transform', 'manage_ingest_pipelines', 'monitor'], + cluster: [ + 'manage_transform', + 'monitor_transform', + 'manage_ingest_pipelines', + 'monitor', + 'manage_index_templates', + ], application: [ { application: 'kibana-.kibana', 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 925c62d97710f..9e62633a0a7d6 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 @@ -148,5 +148,29 @@ if (ctx.entity?.metadata?.sourceIndex != null) { "index_name_prefix": ".entities.v1.history.admin-console-services.", }, }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services@platform", + }, + }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services-history@platform", + }, + }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services@custom", + }, + }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services-history@custom", + }, + }, ] `; 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 69e63abd0cb94..e96d7366e7e04 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 @@ -108,5 +108,29 @@ ctx.event.category = ctx.entity.identity.event.category.keySet().toArray()[0];", "value": ".entities.v1.latest.admin-console-services", }, }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services@platform", + }, + }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services-latest@platform", + }, + }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services@custom", + }, + }, + Object { + "pipeline": Object { + "ignore_missing_pipeline": true, + "name": "admin-console-services-latest@custom", + }, + }, ] `; 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 45ee008f9c6b5..43f18b2b81bf0 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 @@ -163,5 +163,30 @@ export function generateHistoryProcessors(definition: EntityDefinition) { date_formats: ['UNIX_MS', 'ISO8601', "yyyy-MM-dd'T'HH:mm:ss.SSSXX"], }, }, + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}@platform`, + }, + }, + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}-history@platform`, + }, + }, + + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}@custom`, + }, + }, + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}-history@custom`, + }, + }, ]; } 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 22b2ac19775a1..b9a18e8b7a2b6 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 @@ -122,5 +122,30 @@ export function generateLatestProcessors(definition: EntityDefinition) { value: `${generateLatestIndexName(definition)}`, }, }, + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}@platform`, + }, + }, + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}-latest@platform`, + }, + }, + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}@custom`, + }, + }, + + { + pipeline: { + ignore_missing_pipeline: true, + name: `${definition.id}-latest@custom`, + }, + }, ]; } 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 8560f0a4f1f4f..95eb63253f40c 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 @@ -34,6 +34,18 @@ const assertHasCreatedDefinition = ( overwrite: true, }); + expect(esClient.indices.putIndexTemplate).toBeCalledTimes(2); + expect(esClient.indices.putIndexTemplate).toBeCalledWith( + expect.objectContaining({ + name: `entities_v1_history_${definition.id}_index_template`, + }) + ); + expect(esClient.indices.putIndexTemplate).toBeCalledWith( + expect.objectContaining({ + name: `entities_v1_latest_${definition.id}_index_template`, + }) + ); + expect(esClient.ingest.putPipeline).toBeCalledTimes(2); expect(esClient.ingest.putPipeline).toBeCalledWith({ id: generateHistoryIngestPipelineId(builtInServicesFromLogsEntityDefinition), @@ -111,6 +123,20 @@ const assertHasUninstalledDefinition = ( expect(esClient.transform.deleteTransform).toBeCalledTimes(2); expect(esClient.ingest.deletePipeline).toBeCalledTimes(2); expect(soClient.delete).toBeCalledTimes(1); + + expect(esClient.indices.deleteIndexTemplate).toBeCalledTimes(2); + expect(esClient.indices.deleteIndexTemplate).toBeCalledWith( + { + name: `entities_v1_history_${definition.id}_index_template`, + }, + { ignore: [404] } + ); + expect(esClient.indices.deleteIndexTemplate).toBeCalledWith( + { + name: `entities_v1_latest_${definition.id}_index_template`, + }, + { ignore: [404] } + ); }; describe('install_entity_definition', () => { diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts index 980c743575fe2..b47f17b6b00fa 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts @@ -9,6 +9,10 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import { EntityDefinition } from '@kbn/entities-schema'; import { Logger } from '@kbn/logging'; +import { + getEntityHistoryIndexTemplateV1, + getEntityLatestIndexTemplateV1, +} from '../../../common/helpers'; import { createAndInstallHistoryIngestPipeline, createAndInstallLatestIngestPipeline, @@ -28,6 +32,9 @@ import { stopAndDeleteLatestTransform, } from './stop_and_delete_transform'; import { uninstallEntityDefinition } from './uninstall_entity_definition'; +import { deleteTemplate, upsertTemplate } from '../manage_index_templates'; +import { getEntitiesLatestIndexTemplateConfig } from '../../templates/entities_latest_template'; +import { getEntitiesHistoryIndexTemplateConfig } from '../../templates/entities_history_template'; export interface InstallDefinitionParams { esClient: ElasticsearchClient; @@ -52,6 +59,10 @@ export async function installEntityDefinition({ latest: false, }, definition: false, + indexTemplates: { + history: false, + latest: false, + }, }; try { @@ -62,6 +73,20 @@ export async function installEntityDefinition({ const entityDefinition = await saveEntityDefinition(soClient, definition); installState.definition = true; + // install scoped index template + await upsertTemplate({ + esClient, + logger, + template: getEntitiesHistoryIndexTemplateConfig(definition.id), + }); + installState.indexTemplates.history = true; + await upsertTemplate({ + esClient, + logger, + template: getEntitiesLatestIndexTemplateConfig(definition.id), + }); + installState.indexTemplates.latest = true; + // install ingest pipelines logger.debug(`Installing ingest pipelines for definition ${definition.id}`); await createAndInstallHistoryIngestPipeline(esClient, entityDefinition, logger); @@ -99,6 +124,21 @@ export async function installEntityDefinition({ await stopAndDeleteLatestTransform(esClient, definition, logger); } + if (installState.indexTemplates.history) { + await deleteTemplate({ + esClient, + logger, + name: getEntityHistoryIndexTemplateV1(definition.id), + }); + } + if (installState.indexTemplates.latest) { + await deleteTemplate({ + esClient, + logger, + name: getEntityLatestIndexTemplateV1(definition.id), + }); + } + throw e; } } diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/uninstall_entity_definition.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/uninstall_entity_definition.ts index 8642ebafa904b..9b8685031642a 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/uninstall_entity_definition.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/uninstall_entity_definition.ts @@ -9,6 +9,10 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import { EntityDefinition } from '@kbn/entities-schema'; import { Logger } from '@kbn/logging'; +import { + getEntityHistoryIndexTemplateV1, + getEntityLatestIndexTemplateV1, +} from '../../../common/helpers'; import { deleteEntityDefinition } from './delete_entity_definition'; import { deleteIndices } from './delete_index'; import { deleteHistoryIngestPipeline, deleteLatestIngestPipeline } from './delete_ingest_pipeline'; @@ -17,6 +21,7 @@ import { stopAndDeleteHistoryTransform, stopAndDeleteLatestTransform, } from './stop_and_delete_transform'; +import { deleteTemplate } from '../manage_index_templates'; export async function uninstallEntityDefinition({ definition, @@ -36,6 +41,9 @@ export async function uninstallEntityDefinition({ await deleteHistoryIngestPipeline(esClient, definition, logger); await deleteLatestIngestPipeline(esClient, definition, logger); await deleteEntityDefinition(soClient, definition, logger); + await deleteTemplate({ esClient, logger, name: getEntityHistoryIndexTemplateV1(definition.id) }); + await deleteTemplate({ esClient, logger, name: getEntityLatestIndexTemplateV1(definition.id) }); + if (deleteData) { await deleteIndices(esClient, definition, logger); } diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/manage_index_templates.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/manage_index_templates.ts index 0f73ba7715bfd..f300df4a92c1d 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/manage_index_templates.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/manage_index_templates.ts @@ -10,6 +10,7 @@ import { IndicesPutIndexTemplateRequest, } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { retryTransientEsErrors } from './entities/helpers/retry'; interface TemplateManagementOptions { esClient: ElasticsearchClient; @@ -23,12 +24,18 @@ interface ComponentManagementOptions { logger: Logger; } +interface DeleteTemplateOptions { + esClient: ElasticsearchClient; + name: string; + logger: Logger; +} + export async function upsertTemplate({ esClient, template, logger }: TemplateManagementOptions) { try { - await esClient.indices.putIndexTemplate(template); + await retryTransientEsErrors(() => esClient.indices.putIndexTemplate(template), { logger }); } catch (error: any) { logger.error(`Error updating entity manager index template: ${error.message}`); - return; + throw error; } logger.info( @@ -37,12 +44,26 @@ export async function upsertTemplate({ esClient, template, logger }: TemplateMan logger.debug(() => `Entity manager index template: ${JSON.stringify(template)}`); } +export async function deleteTemplate({ esClient, name, logger }: DeleteTemplateOptions) { + try { + await retryTransientEsErrors( + () => esClient.indices.deleteIndexTemplate({ name }, { ignore: [404] }), + { logger } + ); + } catch (error: any) { + logger.error(`Error deleting entity manager index template: ${error.message}`); + throw error; + } +} + export async function upsertComponent({ esClient, component, logger }: ComponentManagementOptions) { try { - await esClient.cluster.putComponentTemplate(component); + await retryTransientEsErrors(() => esClient.cluster.putComponentTemplate(component), { + logger, + }); } catch (error: any) { logger.error(`Error updating entity manager component template: ${error.message}`); - return; + throw error; } logger.info( diff --git a/x-pack/plugins/observability_solution/entity_manager/server/plugin.ts b/x-pack/plugins/observability_solution/entity_manager/server/plugin.ts index 3a51988841766..80154149e2402 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/plugin.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/plugin.ts @@ -14,7 +14,7 @@ import { PluginConfigDescriptor, Logger, } from '@kbn/core/server'; -import { upsertComponent, upsertTemplate } from './lib/manage_index_templates'; +import { upsertComponent } from './lib/manage_index_templates'; import { setupRoutes } from './routes'; import { EntityManagerPluginSetupDependencies, @@ -27,8 +27,6 @@ import { entityDefinition, EntityDiscoveryApiKeyType } from './saved_objects'; import { entitiesEntityComponentTemplateConfig } from './templates/components/entity'; import { entitiesLatestBaseComponentTemplateConfig } from './templates/components/base_latest'; import { entitiesHistoryBaseComponentTemplateConfig } from './templates/components/base_history'; -import { entitiesHistoryIndexTemplateConfig } from './templates/entities_history_template'; -import { entitiesLatestIndexTemplateConfig } from './templates/entities_latest_template'; export type EntityManagerServerPluginSetup = ReturnType; export type EntityManagerServerPluginStart = ReturnType; @@ -113,22 +111,7 @@ export class EntityManagerServerPlugin logger: this.logger, component: entitiesEntityComponentTemplateConfig, }), - ]) - .then(() => - upsertTemplate({ - esClient, - logger: this.logger, - template: entitiesHistoryIndexTemplateConfig, - }) - ) - .then(() => - upsertTemplate({ - esClient, - logger: this.logger, - template: entitiesLatestIndexTemplateConfig, - }) - ) - .catch(() => {}); + ]).catch(() => {}); return {}; } diff --git a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/create.ts b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/create.ts index 8d17debc8914d..9d38cc7c5e716 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/create.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/create.ts @@ -7,7 +7,7 @@ import { RequestHandlerContext } from '@kbn/core/server'; import { EntityDefinition, entityDefinitionSchema } from '@kbn/entities-schema'; -import { stringifyZodError } from '@kbn/zod-helpers'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { SetupRouteOptions } from '../types'; import { EntityIdConflict } from '../../lib/entities/errors/entity_id_conflict_error'; import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; @@ -23,13 +23,7 @@ export function createEntityDefinitionRoute({ { path: '/internal/entities/definition', validate: { - body: (body, res) => { - try { - return res.ok(entityDefinitionSchema.parse(body)); - } catch (e) { - return res.badRequest(stringifyZodError(e)); - } - }, + body: buildRouteValidationWithZod(entityDefinitionSchema.strict()), }, }, async (context, req, res) => { diff --git a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/delete.ts b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/delete.ts index f79fdce2368c6..b0c423a47a4b9 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/delete.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/delete.ts @@ -6,7 +6,11 @@ */ import { RequestHandlerContext } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; +import { + deleteEntityDefinitionParamsSchema, + deleteEntityDefinitionQuerySchema, +} from '@kbn/entities-schema'; import { SetupRouteOptions } from '../types'; import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; @@ -22,12 +26,8 @@ export function deleteEntityDefinitionRoute({ { path: '/internal/entities/definition/{id}', validate: { - params: schema.object({ - id: schema.string(), - }), - query: schema.object({ - deleteData: schema.maybe(schema.boolean({ defaultValue: false })), - }), + params: buildRouteValidationWithZod(deleteEntityDefinitionParamsSchema.strict()), + query: buildRouteValidationWithZod(deleteEntityDefinitionQuerySchema.strict()), }, }, async (context, req, res) => { diff --git a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/get.ts b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/get.ts index 25a593c05209e..3f1ffde5afef4 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/get.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/get.ts @@ -6,7 +6,8 @@ */ import { RequestHandlerContext } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; +import { getEntityDefinitionQuerySchema } from '@kbn/entities-schema'; import { SetupRouteOptions } from '../types'; import { findEntityDefinitions } from '../../lib/entities/find_entity_definition'; @@ -17,10 +18,7 @@ export function getEntityDefinitionRoute({ { path: '/internal/entities/definition', validate: { - query: schema.object({ - page: schema.maybe(schema.number()), - perPage: schema.maybe(schema.number()), - }), + query: buildRouteValidationWithZod(getEntityDefinitionQuerySchema.strict()), }, }, async (context, req, res) => { diff --git a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/reset.ts b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/reset.ts index ffa85931a3bef..6f97a5fbe0d51 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/reset.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/routes/entities/reset.ts @@ -6,7 +6,8 @@ */ import { RequestHandlerContext } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; +import { resetEntityDefinitionParamsSchema } from '@kbn/entities-schema'; import { SetupRouteOptions } from '../types'; import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; @@ -39,9 +40,7 @@ export function resetEntityDefinitionRoute({ { path: '/internal/entities/definition/{id}/_reset', validate: { - params: schema.object({ - id: schema.string(), - }), + params: buildRouteValidationWithZod(resetEntityDefinitionParamsSchema.strict()), }, }, async (context, req, res) => { diff --git a/x-pack/plugins/observability_solution/entity_manager/server/templates/components/helpers.test.ts b/x-pack/plugins/observability_solution/entity_manager/server/templates/components/helpers.test.ts new file mode 100644 index 0000000000000..3321ee39edeb4 --- /dev/null +++ b/x-pack/plugins/observability_solution/entity_manager/server/templates/components/helpers.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 { getCustomHistoryTemplateComponents, getCustomLatestTemplateComponents } from './helpers'; + +describe('helpers', () => { + it('getCustomLatestTemplateComponents should return template component in the right sort order', () => { + const definitionId = 'test'; + const result = getCustomLatestTemplateComponents(definitionId); + expect(result).toEqual([ + 'test@platform', + 'test-latest@platform', + 'test@custom', + 'test-latest@custom', + ]); + }); + + it('getCustomHistoryTemplateComponents should return template component in the right sort order', () => { + const definitionId = 'test'; + const result = getCustomHistoryTemplateComponents(definitionId); + expect(result).toEqual([ + 'test@platform', + 'test-history@platform', + 'test@custom', + 'test-history@custom', + ]); + }); +}); diff --git a/x-pack/plugins/observability_solution/entity_manager/server/templates/components/helpers.ts b/x-pack/plugins/observability_solution/entity_manager/server/templates/components/helpers.ts new file mode 100644 index 0000000000000..e976a216da97b --- /dev/null +++ b/x-pack/plugins/observability_solution/entity_manager/server/templates/components/helpers.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. + */ + +export const getCustomLatestTemplateComponents = (definitionId: string) => [ + `${definitionId}@platform`, // @platform goes before so it can be overwritten by custom + `${definitionId}-latest@platform`, + `${definitionId}@custom`, + `${definitionId}-latest@custom`, +]; + +export const getCustomHistoryTemplateComponents = (definitionId: string) => [ + `${definitionId}@platform`, // @platform goes before so it can be overwritten by custom + `${definitionId}-history@platform`, + `${definitionId}@custom`, + `${definitionId}-history@custom`, +]; diff --git a/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_history_template.ts b/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_history_template.ts index d5ceeecd44828..63d589bfaa754 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_history_template.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_history_template.ts @@ -6,29 +6,35 @@ */ import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { getEntityHistoryIndexTemplateV1 } from '../../common/helpers'; import { ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, ENTITY_HISTORY_BASE_COMPONENT_TEMPLATE_V1, ENTITY_HISTORY_INDEX_PREFIX_V1, - ENTITY_HISTORY_INDEX_TEMPLATE_V1, } from '../../common/constants_entities'; +import { getCustomHistoryTemplateComponents } from './components/helpers'; -export const entitiesHistoryIndexTemplateConfig: IndicesPutIndexTemplateRequest = { - name: ENTITY_HISTORY_INDEX_TEMPLATE_V1, +export const getEntitiesHistoryIndexTemplateConfig = ( + definitionId: string +): IndicesPutIndexTemplateRequest => ({ + name: getEntityHistoryIndexTemplateV1(definitionId), _meta: { description: "Index template for indices managed by the Elastic Entity Model's entity discovery framework for the history dataset", ecs_version: '8.0.0', managed: true, + managed_by: 'elastic_entity_model', }, + ignore_missing_component_templates: getCustomHistoryTemplateComponents(definitionId), composed_of: [ ENTITY_HISTORY_BASE_COMPONENT_TEMPLATE_V1, ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, + ...getCustomHistoryTemplateComponents(definitionId), ], index_patterns: [`${ENTITY_HISTORY_INDEX_PREFIX_V1}.*`], - priority: 1, + priority: 200, template: { mappings: { _meta: { @@ -72,4 +78,4 @@ export const entitiesHistoryIndexTemplateConfig: IndicesPutIndexTemplateRequest }, }, }, -}; +}); diff --git a/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_latest_template.ts b/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_latest_template.ts index f601c3aa9d57d..3ad09e7257a1a 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_latest_template.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/templates/entities_latest_template.ts @@ -6,26 +6,32 @@ */ import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { getEntityLatestIndexTemplateV1 } from '../../common/helpers'; import { ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, ENTITY_LATEST_BASE_COMPONENT_TEMPLATE_V1, ENTITY_LATEST_INDEX_PREFIX_V1, - ENTITY_LATEST_INDEX_TEMPLATE_V1, } from '../../common/constants_entities'; +import { getCustomLatestTemplateComponents } from './components/helpers'; -export const entitiesLatestIndexTemplateConfig: IndicesPutIndexTemplateRequest = { - name: ENTITY_LATEST_INDEX_TEMPLATE_V1, +export const getEntitiesLatestIndexTemplateConfig = ( + definitionId: string +): IndicesPutIndexTemplateRequest => ({ + name: getEntityLatestIndexTemplateV1(definitionId), _meta: { description: "Index template for indices managed by the Elastic Entity Model's entity discovery framework for the latest dataset", ecs_version: '8.0.0', managed: true, + managed_by: 'elastic_entity_model', }, + ignore_missing_component_templates: getCustomLatestTemplateComponents(definitionId), composed_of: [ ENTITY_LATEST_BASE_COMPONENT_TEMPLATE_V1, ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, + ...getCustomLatestTemplateComponents(definitionId), ], index_patterns: [`${ENTITY_LATEST_INDEX_PREFIX_V1}.*`], priority: 1, @@ -72,4 +78,4 @@ export const entitiesLatestIndexTemplateConfig: IndicesPutIndexTemplateRequest = }, }, }, -}; +}); diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/profiling/functions.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/profiling/functions.tsx index 811172fca2695..5dee9438b280c 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/profiling/functions.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/profiling/functions.tsx @@ -31,11 +31,13 @@ export function Functions({ kuery }: Props) { const { dateRange, getDateRangeInTimestamp } = useDatePickerContext(); const { from, to } = getDateRangeInTimestamp(); const { request$ } = useRequestObservable(); + const { renderMode } = useAssetDetailsRenderPropsContext(); const profilingLinkLocator = services.observabilityShared.locators.profiling.topNFunctionsLocator; const profilingLinkLabel = i18n.translate('xpack.infra.flamegraph.profilingAppTopFunctionsLink', { defaultMessage: 'Go to Universal Profiling Functions', }); + const showFullScreenSelector = renderMode.mode === 'page'; const params = useMemo( () => ({ @@ -86,6 +88,7 @@ export function Functions({ kuery }: Props) { rangeFrom={from} rangeTo={to} height="60vh" + showFullScreenSelector={showFullScreenSelector} /> ); diff --git a/x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/embeddable_functions.tsx b/x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/embeddable_functions.tsx index c4fad57b890ce..aa5d3c335ec05 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/embeddable_functions.tsx +++ b/x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/embeddable_functions.tsx @@ -17,6 +17,7 @@ interface Props { rangeFrom: number; rangeTo: number; height?: string; + showFullScreenSelector?: boolean; } export function EmbeddableFunctions(props: Props) { diff --git a/x-pack/plugins/observability_solution/profiling/public/components/topn_functions/index.tsx b/x-pack/plugins/observability_solution/profiling/public/components/topn_functions/index.tsx index 4f56865d54a84..cad716654a843 100644 --- a/x-pack/plugins/observability_solution/profiling/public/components/topn_functions/index.tsx +++ b/x-pack/plugins/observability_solution/profiling/public/components/topn_functions/index.tsx @@ -32,6 +32,7 @@ interface Props { comparisonTopNFunctions?: TopNFunctions; totalSeconds: number; isDifferentialView: boolean; + showFullScreenSelector?: boolean; baselineScaleFactor?: number; comparisonScaleFactor?: number; onFrameClick?: (functionName: string) => void; @@ -50,6 +51,7 @@ export const TopNFunctionsGrid = ({ topNFunctions, comparisonTopNFunctions, totalSeconds, + showFullScreenSelector = true, isDifferentialView, baselineScaleFactor, comparisonScaleFactor, @@ -316,7 +318,7 @@ export const TopNFunctionsGrid = ({ showColumnSelector: false, showKeyboardShortcuts: !isDifferentialView, showDisplaySelector: !isDifferentialView, - showFullScreenSelector: !isDifferentialView, + showFullScreenSelector: showFullScreenSelector && !isDifferentialView, showSortSelector: false, }} virtualizationOptions={{ diff --git a/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions.tsx b/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions.tsx index e281f12224c8f..851eeeb8fe103 100644 --- a/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions.tsx +++ b/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions.tsx @@ -23,6 +23,7 @@ export interface FunctionsProps { isLoading: boolean; rangeFrom: number; rangeTo: number; + showFullScreenSelector?: boolean; } export function EmbeddableFunctions({ @@ -30,6 +31,7 @@ export function EmbeddableFunctions({ isLoading, rangeFrom, rangeTo, + showFullScreenSelector, ...deps }: EmbeddableFunctionsProps) { const totalSeconds = useMemo(() => (rangeTo - rangeFrom) / 1000, [rangeFrom, rangeTo]); @@ -37,7 +39,11 @@ export function EmbeddableFunctions({
- +
diff --git a/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions_grid.tsx b/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions_grid.tsx index 5579217a59d22..8b4dfd5d62c27 100644 --- a/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions_grid.tsx +++ b/x-pack/plugins/observability_solution/profiling/public/embeddables/functions/embeddable_functions_grid.tsx @@ -13,9 +13,10 @@ import { TopNFunctionsGrid } from '../../components/topn_functions'; interface Props { data?: TopNFunctions; totalSeconds: number; + showFullScreenSelector?: boolean; } -export function EmbeddableFunctionsGrid({ data, totalSeconds }: Props) { +export function EmbeddableFunctionsGrid({ data, totalSeconds, showFullScreenSelector }: Props) { const [sortField, setSortField] = useState(TopNFunctionSortField.Rank); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc'); const [pageIndex, setPageIndex] = useState(0); @@ -34,6 +35,7 @@ export function EmbeddableFunctionsGrid({ data, totalSeconds }: Props) { setSortDirection(sorting.direction); }} isEmbedded + showFullScreenSelector={showFullScreenSelector} /> ); } diff --git a/x-pack/plugins/observability_solution/synthetics/kibana.jsonc b/x-pack/plugins/observability_solution/synthetics/kibana.jsonc index 3e0715e48abb1..c527630b85d6c 100644 --- a/x-pack/plugins/observability_solution/synthetics/kibana.jsonc +++ b/x-pack/plugins/observability_solution/synthetics/kibana.jsonc @@ -17,6 +17,7 @@ "embeddable", "discover", "dataViews", + "dashboard", "encryptedSavedObjects", "exploratoryView", "features", @@ -31,7 +32,9 @@ "triggersActionsUi", "usageCollection", "bfetch", - "unifiedSearch" + "uiActions", + "unifiedSearch", + "presentationUtil" ], "optionalPlugins": [ "cloud", @@ -53,7 +56,7 @@ "observability", "spaces", "indexLifecycleManagement", - "unifiedDocViewer" + "unifiedDocViewer", ] } } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/constants.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/constants.ts new file mode 100644 index 0000000000000..b471d46ac3832 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/constants.ts @@ -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 const SYNTHETICS_OVERVIEW_EMBEDDABLE = 'SYNTHETICS_OVERVIEW_EMBEDDABLE'; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/register_embeddables.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/register_embeddables.ts new file mode 100644 index 0000000000000..fbc516a6f611b --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/register_embeddables.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreSetup } from '@kbn/core-lifecycle-browser'; + +import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-browser/src'; +import { createStatusOverviewPanelAction } from './ui_actions/create_overview_panel_action'; +import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin'; +import { SYNTHETICS_OVERVIEW_EMBEDDABLE } from './constants'; + +export const registerSyntheticsEmbeddables = ( + core: CoreSetup, + pluginsSetup: ClientPluginsSetup +) => { + pluginsSetup.embeddable.registerReactEmbeddableFactory( + SYNTHETICS_OVERVIEW_EMBEDDABLE, + async () => { + const { getStatusOverviewEmbeddableFactory } = await import( + './status_overview/status_overview_embeddable_factory' + ); + return getStatusOverviewEmbeddableFactory(core.getStartServices); + } + ); + + const { uiActions, cloud, serverless } = pluginsSetup; + + // Initialize actions + const addOverviewPanelAction = createStatusOverviewPanelAction(); + + core.getStartServices().then(([_, pluginsStart]) => { + pluginsStart.dashboard.registerDashboardPanelPlacementSetting( + SYNTHETICS_OVERVIEW_EMBEDDABLE, + () => { + return { width: 10, height: 8 }; + } + ); + }); + + // Assign triggers + // Only register these actions in stateful kibana, and the serverless observability project + if (Boolean((serverless && cloud?.serverless.projectType === 'observability') || !serverless)) { + uiActions.addTriggerAction(ADD_PANEL_TRIGGER, addOverviewPanelAction); + } +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/status_overview/status_overview_component.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/status_overview/status_overview_component.tsx new file mode 100644 index 0000000000000..1034f9ea959ec --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/status_overview/status_overview_component.tsx @@ -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 React from 'react'; +import { Subject } from 'rxjs'; +import { OverviewStatus } from '../../synthetics/components/monitors_page/overview/overview/overview_status'; +import { SyntheticsEmbeddableContext } from '../synthetics_embeddable_context'; + +export const StatusOverviewComponent = ({ reload$ }: { reload$: Subject }) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/status_overview/status_overview_embeddable_factory.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/status_overview/status_overview_embeddable_factory.tsx new file mode 100644 index 0000000000000..3f7b3fcf13699 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/status_overview/status_overview_embeddable_factory.tsx @@ -0,0 +1,99 @@ +/* + * 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'; + +import React, { useEffect } from 'react'; +import { DefaultEmbeddableApi, ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { + initializeTitles, + useBatchedPublishingSubjects, + fetch$, + PublishesWritablePanelTitle, + PublishesPanelTitle, + SerializedTitles, +} from '@kbn/presentation-publishing'; +import { BehaviorSubject, Subject } from 'rxjs'; +import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; +import { SYNTHETICS_OVERVIEW_EMBEDDABLE } from '../constants'; +import { ClientPluginsStart } from '../../../plugin'; +import { StatusOverviewComponent } from './status_overview_component'; + +export const getOverviewPanelTitle = () => + i18n.translate('xpack.synthetics.statusOverview.displayName', { + defaultMessage: 'Synthetics Status Overview', + }); + +export type OverviewEmbeddableState = SerializedTitles; + +export type StatusOverviewApi = DefaultEmbeddableApi & + PublishesWritablePanelTitle & + PublishesPanelTitle; + +export const getStatusOverviewEmbeddableFactory = ( + getStartServices: StartServicesAccessor +) => { + const factory: ReactEmbeddableFactory< + OverviewEmbeddableState, + OverviewEmbeddableState, + StatusOverviewApi + > = { + type: SYNTHETICS_OVERVIEW_EMBEDDABLE, + deserializeState: (state) => { + return state.rawState as OverviewEmbeddableState; + }, + buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const defaultTitle$ = new BehaviorSubject(getOverviewPanelTitle()); + const reload$ = new Subject(); + + const api = buildApi( + { + ...titlesApi, + defaultPanelTitle: defaultTitle$, + serializeState: () => { + return { + rawState: { + ...serializeTitles(), + }, + }; + }, + }, + { + ...titleComparators, + } + ); + + const fetchSubscription = fetch$(api) + .pipe() + .subscribe((next) => { + reload$.next(next.isReload); + }); + + return { + api, + Component: () => { + const [] = useBatchedPublishingSubjects(); + + useEffect(() => { + return () => { + fetchSubscription.unsubscribe(); + }; + }, []); + return ( +
+ +
+ ); + }, + }; + }, + }; + return factory; +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/synthetics_embeddable_context.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/synthetics_embeddable_context.tsx new file mode 100644 index 0000000000000..0953fb79961b1 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/synthetics_embeddable_context.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 { createBrowserHistory } from 'history'; +import { EuiPanel } from '@elastic/eui'; +import { Router } from '@kbn/shared-ux-router'; +import { SyntheticsSharedContext } from '../synthetics/contexts/synthetics_shared_context'; +import { SyntheticsEmbeddableStateContextProvider } from '../synthetics/contexts/synthetics_embeddable_context'; +import { getSyntheticsAppProps } from '../synthetics/render_app'; +import { SyntheticsSettingsContextProvider } from '../synthetics/contexts'; + +export const SyntheticsEmbeddableContext: React.FC<{ search?: string }> = ({ + search, + children, +}) => { + const props = getSyntheticsAppProps(); + + return ( + + + + + {children} + + + + + ); +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/ui_actions/create_overview_panel_action.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/ui_actions/create_overview_panel_action.tsx new file mode 100644 index 0000000000000..202a09e1f3576 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/embeddables/ui_actions/create_overview_panel_action.tsx @@ -0,0 +1,54 @@ +/* + * 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'; +import { apiIsPresentationContainer } from '@kbn/presentation-containers'; +import { + IncompatibleActionError, + type UiActionsActionDefinition, +} from '@kbn/ui-actions-plugin/public'; +import { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { SYNTHETICS_OVERVIEW_EMBEDDABLE } from '../constants'; + +export const COMMON_SYNTHETICS_GROUPING = [ + { + id: 'synthetics', + getDisplayName: () => + i18n.translate('xpack.synthetics.common.constants.grouping.legacy', { + defaultMessage: 'Synthetics', + }), + getIconType: () => { + return 'online'; + }, + }, +]; +export const ADD_SYNTHETICS_OVERVIEW_ACTION_ID = 'CREATE_SYNTHETICS_OVERVIEW_EMBEDDABLE'; + +export function createStatusOverviewPanelAction(): UiActionsActionDefinition { + return { + id: ADD_SYNTHETICS_OVERVIEW_ACTION_ID, + grouping: COMMON_SYNTHETICS_GROUPING, + order: 30, + getIconType: () => 'online', + isCompatible: async ({ embeddable }) => { + return apiIsPresentationContainer(embeddable); + }, + execute: async ({ embeddable }) => { + if (!apiIsPresentationContainer(embeddable)) throw new IncompatibleActionError(); + try { + embeddable.addNewPanel({ + panelType: SYNTHETICS_OVERVIEW_EMBEDDABLE, + }); + } catch (e) { + return Promise.reject(); + } + }, + getDisplayName: () => + i18n.translate('xpack.synthetics.syntheticsEmbeddable.ariaLabel', { + defaultMessage: 'Synthetics Overview', + }), + }; +} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/alerting_callout/alerting_callout.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/alerting_callout/alerting_callout.tsx index 07c594aa56156..4b508ab701231 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/alerting_callout/alerting_callout.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/alerting_callout/alerting_callout.tsx @@ -14,6 +14,8 @@ import { useFetcher } from '@kbn/observability-shared-plugin/public'; import { useSessionStorage } from 'react-use'; import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ClientPluginsStart } from '../../../../../plugin'; import { selectDynamicSettings } from '../../../state/settings'; import { selectSyntheticsAlerts, @@ -21,7 +23,7 @@ import { } from '../../../state/alert_rules/selectors'; import { selectMonitorListState } from '../../../state'; import { getDynamicSettingsAction } from '../../../state/settings/actions'; -import { useSyntheticsSettingsContext, useSyntheticsStartPlugins } from '../../../contexts'; +import { useSyntheticsSettingsContext } from '../../../contexts'; import { ConfigKey } from '../../../../../../common/runtime_types'; export const AlertingCallout = ({ isAlertingEnabled }: { isAlertingEnabled?: boolean }) => { @@ -40,7 +42,8 @@ export const AlertingCallout = ({ isAlertingEnabled }: { isAlertingEnabled?: boo loaded: monitorsLoaded, } = useSelector(selectMonitorListState); - const syntheticsLocators = useSyntheticsStartPlugins()?.share?.url.locators; + const syntheticsLocators = useKibana().services.share?.url.locators; + const locator = syntheticsLocators?.get(syntheticsSettingsLocatorID); const { data: url } = useFetcher(() => { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/components/embeddable_panel_wrapper.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/components/embeddable_panel_wrapper.tsx new file mode 100644 index 0000000000000..cd73097c956a6 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/components/embeddable_panel_wrapper.tsx @@ -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 React, { FC, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonIcon, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiPopover, + EuiProgress, + EuiTitle, +} from '@elastic/eui'; +import { + LazySavedObjectSaveModalDashboard, + SaveModalDashboardProps, + withSuspense, +} from '@kbn/presentation-util-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { SYNTHETICS_OVERVIEW_EMBEDDABLE } from '../../../../embeddables/constants'; +import { ClientPluginsStart } from '../../../../../plugin'; + +const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard); + +export const EmbeddablePanelWrapper: FC<{ + title: string; + loading?: boolean; +}> = ({ children, title, loading }) => { + const [isPopoverOpen, setIsPopoverOpen] = React.useState(false); + + const [isDashboardAttachmentReady, setDashboardAttachmentReady] = React.useState(false); + + const closePopover = () => { + setIsPopoverOpen(false); + }; + + const { embeddable } = useKibana().services; + + const isSyntheticsApp = window.location.pathname.includes('/app/synthetics'); + + const handleAttachToDashboardSave: SaveModalDashboardProps['onSave'] = useCallback( + ({ dashboardId, newTitle, newDescription }) => { + const stateTransfer = embeddable.getStateTransfer(); + const embeddableInput = {}; + + const state = { + input: embeddableInput, + type: SYNTHETICS_OVERVIEW_EMBEDDABLE, + }; + + const path = dashboardId === 'new' ? '#/create' : `#/view/${dashboardId}`; + + stateTransfer.navigateToWithEmbeddablePackage('dashboards', { + state, + path, + }); + }, + [embeddable] + ); + + return ( + <> + + {loading && } + + + +

{title}

+
+
+ {isSyntheticsApp && ( + + setIsPopoverOpen(!isPopoverOpen)} + /> + } + isOpen={isPopoverOpen} + closePopover={closePopover} + > + { + setDashboardAttachmentReady(true); + closePopover(); + }} + > + {i18n.translate( + 'xpack.synthetics.embeddablePanelWrapper.shareContextMenuItemLabel', + { defaultMessage: 'Add to dashboard' } + )} + , + ]} + /> + + + )} +
+ + {children} +
+ {isDashboardAttachmentReady ? ( + { + setDashboardAttachmentReady(false); + }} + onSave={handleAttachToDashboardSave} + /> + ) : null} + + ); +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx index a5eaeacf6c7ed..1edaf76ea95a8 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/date_picker/synthetics_date_picker.tsx @@ -7,20 +7,18 @@ import React, { useContext, useEffect } from 'react'; import { EuiSuperDatePicker } from '@elastic/eui'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ClientPluginsStart } from '../../../../../plugin'; import { useUrlParams } from '../../../hooks'; import { CLIENT_DEFAULTS } from '../../../../../../common/constants'; -import { - SyntheticsSettingsContext, - SyntheticsStartupPluginsContext, - SyntheticsRefreshContext, -} from '../../../contexts'; +import { SyntheticsSettingsContext, SyntheticsRefreshContext } from '../../../contexts'; export const SyntheticsDatePicker = ({ fullWidth }: { fullWidth?: boolean }) => { const [getUrlParams, updateUrl] = useUrlParams(); const { commonlyUsedRanges } = useContext(SyntheticsSettingsContext); const { refreshApp } = useContext(SyntheticsRefreshContext); - const { data } = useContext(SyntheticsStartupPluginsContext); + const { data } = useKibana().services; // read time from state and update the url const sharedTimeState = data?.query.timefilter.timefilter.getTime(); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/step_field_trend/step_field_trend.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/step_field_trend/step_field_trend.tsx index 7b687929b78a5..47794a31b8e1c 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/step_field_trend/step_field_trend.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/common/step_field_trend/step_field_trend.tsx @@ -12,9 +12,9 @@ import moment from 'moment'; import { AllSeries, createExploratoryViewUrl } from '@kbn/exploratory-view-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ClientPluginsStart } from '../../../../../plugin'; import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants'; import { JourneyStep } from '../../../../../../common/runtime_types'; -import { useSyntheticsStartPlugins } from '../../../contexts'; export const getLast48Intervals = (activeStep: JourneyStep) => { const timestamp = activeStep['@timestamp']; @@ -36,7 +36,7 @@ export function StepFieldTrend({ field: string; step: JourneyStep; }) { - const { exploratoryView } = useSyntheticsStartPlugins(); + const exploratoryView = useKibana().services.exploratoryView; const EmbeddableExpView = exploratoryView!.ExploratoryViewEmbeddable; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx index 9c51fdf6b7b6e..2587fe21fba21 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/getting_started_page.test.tsx @@ -179,7 +179,7 @@ describe('GettingStartedPage', () => { }); // page is loaded - expect(kibanaService.core.application.navigateToApp).toHaveBeenCalledWith('synthetics', { + expect(kibanaService.coreStart.application.navigateToApp).toHaveBeenCalledWith('synthetics', { path: '/monitors', }); }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts index e4015386ff333..250b6442ce1d1 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/getting_started/use_simple_monitor.ts @@ -56,14 +56,14 @@ export const useSimpleMonitor = ({ monitorData }: { monitorData?: SimpleFormData }, [monitorData]); useEffect(() => { - const { core, toasts } = kibanaService; + const { coreStart, toasts } = kibanaService; const newMonitor = data as UpsertMonitorResponse; const hasErrors = data && 'attributes' in data && data.attributes.errors?.length > 0; if (hasErrors && !loading) { showSyncErrors( (data as { attributes: { errors: ServiceLocationErrors } })?.attributes.errors ?? [], serviceLocations, - core + coreStart ); } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx index 1ba3cec885889..44def6edee978 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_add_edit/hooks/use_monitor_save.tsx @@ -45,7 +45,7 @@ export const useMonitorSave = ({ monitorData }: { monitorData?: SyntheticsMonito }, [monitorData]); useEffect(() => { - const { core, toasts } = kibanaService; + const { coreStart, toasts } = kibanaService; if (status === FETCH_STATUS.FAILURE && error) { toasts.addError( @@ -64,7 +64,7 @@ export const useMonitorSave = ({ monitorData }: { monitorData?: SyntheticsMonito

{monitorId ? MONITOR_UPDATED_SUCCESS_LABEL_SUBTEXT : MONITOR_SUCCESS_LABEL_SUBTEXT}

, - core + coreStart ), toastLifeTimeMs: 3000, }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts index b06b66f8a5a73..562d651cf819b 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/hooks/use_overview_status.ts @@ -18,7 +18,7 @@ import { export function useOverviewStatus({ scopeStatusByLocation }: { scopeStatusByLocation: boolean }) { const pageState = useSelector(selectOverviewPageState); - const { status, error, loaded } = useSelector(selectOverviewStatus); + const { status, error, loaded, loading } = useSelector(selectOverviewStatus); const { lastRefresh } = useSyntheticsRefreshContext(); @@ -37,5 +37,6 @@ export function useOverviewStatus({ scopeStatusByLocation }: { scopeStatusByLoca return { status, error, + loading, }; } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx index b16829fa5880a..dab31650aec4d 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/delete_monitor.tsx @@ -42,7 +42,7 @@ export const DeleteMonitor = ({ }, [configId, isDeleting]); useEffect(() => { - const { core, toasts } = kibanaService; + const { coreStart, toasts } = kibanaService; if (!isDeleting) { return; } @@ -53,7 +53,7 @@ export const DeleteMonitor = ({

{labels.MONITOR_DELETE_FAILURE_LABEL}

, - core + coreStart ), }, { toastLifeTimeMs: 3000 } @@ -72,7 +72,7 @@ export const DeleteMonitor = ({ } )}

, - core + coreStart ), }, { toastLifeTimeMs: 3000 } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx index 53f14deb0dab5..49f21acc20f10 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState, useRef, memo, useCallback } from 'react'; +import React, { useState, useRef, memo, useCallback, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { i18n } from '@kbn/i18n'; import { @@ -15,11 +15,12 @@ import { EuiButtonEmpty, EuiText, } from '@elastic/eui'; -import { selectOverviewStatus } from '../../../../state/overview_status'; +import { useOverviewStatus } from '../../hooks/use_overview_status'; import { useInfiniteScroll } from './use_infinite_scroll'; import { GridItemsByGroup } from './grid_by_group/grid_items_by_group'; import { GroupFields } from './grid_by_group/group_fields'; import { + fetchMonitorOverviewAction, quietFetchOverviewAction, selectOverviewState, setFlyoutConfig, @@ -33,7 +34,7 @@ import { NoMonitorsFound } from '../../common/no_monitors_found'; import { MonitorDetailFlyout } from './monitor_detail_flyout'; export const OverviewGrid = memo(() => { - const { status } = useSelector(selectOverviewStatus); + const { status } = useOverviewStatus({ scopeStatusByLocation: true }); const { data: { monitors }, @@ -49,6 +50,11 @@ export const OverviewGrid = memo(() => { const intersectionRef = useRef(null); const { monitorsSortedByStatus } = useMonitorsSortedByStatus(); + // fetch overview for all other page state changes + useEffect(() => { + dispatch(fetchMonitorOverviewAction.get(pageState)); + }, [dispatch, pageState]); + const setFlyoutConfigCallback = useCallback( (params: FlyoutParamProps) => dispatch(setFlyoutConfig(params)), [dispatch] diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx index fd3e19c7e96f0..28eaf97a37c5f 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_status.tsx @@ -5,10 +5,13 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiStat, EuiTitle } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiStat } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { useDispatch } from 'react-redux'; +import { Subject } from 'rxjs'; +import { useSyntheticsRefreshContext } from '../../../../contexts'; +import { EmbeddablePanelWrapper } from '../../../common/components/embeddable_panel_wrapper'; import { clearOverviewStatusErrorAction } from '../../../../state/overview_status'; import { kibanaService } from '../../../../../../utils/kibana_service'; import { useGetUrlParams } from '../../../../hooks/use_url_params'; @@ -18,10 +21,16 @@ function title(t?: number) { return t ?? '-'; } -export function OverviewStatus() { +export function OverviewStatus({ reload$ }: { reload$?: Subject }) { const { statusFilter } = useGetUrlParams(); - const { status, error: statusError } = useOverviewStatus({ scopeStatusByLocation: true }); + const { refreshApp } = useSyntheticsRefreshContext(); + + const { + status, + error: statusError, + loading, + } = useOverviewStatus({ scopeStatusByLocation: true }); const dispatch = useDispatch(); const [statusConfig, setStatusConfig] = useState({ up: status?.up, @@ -30,6 +39,14 @@ export function OverviewStatus() { disabledCount: status?.disabledCount, }); + useEffect(() => { + const sub = reload$?.subscribe(() => { + refreshApp(); + }); + + return () => sub?.unsubscribe(); + }, [refreshApp, reload$]); + useEffect(() => { if (statusError) { dispatch(clearOverviewStatusErrorAction()); @@ -87,10 +104,7 @@ export function OverviewStatus() { }, [status, statusFilter]); return ( - - -

{headingText}

-
+ @@ -136,7 +150,7 @@ export function OverviewStatus() { )} -
+ ); } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx index f2e86f077123d..814fb13a99ba9 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/settings/global_params/delete_param.tsx @@ -48,7 +48,7 @@ export const DeleteParam = ({ if (!isDeleting) { return; } - const { core, toasts } = kibanaService; + const { coreStart, toasts } = kibanaService; if (status === FETCH_STATUS.FAILURE) { toasts.addDanger( @@ -61,7 +61,7 @@ export const DeleteParam = ({ values: { name }, })}

, - core + coreStart ), }, { toastLifeTimeMs: 3000 } @@ -76,7 +76,7 @@ export const DeleteParam = ({ values: { name }, })}

, - core + coreStart ), }, { toastLifeTimeMs: 3000 } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_marker/waterfall_marker_test_helper.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_marker/waterfall_marker_test_helper.tsx index b30cd29a0f065..93afcb3c55bbb 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_marker/waterfall_marker_test_helper.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_marker/waterfall_marker_test_helper.tsx @@ -5,8 +5,9 @@ * 2.0. */ +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import React from 'react'; -import { SyntheticsStartupPluginsContext } from '../../../../../contexts'; +import { i18n } from '@kbn/i18n'; import { JourneyStep } from '../../../../../../../../common/runtime_types'; import { WaterfallContext } from '../context/waterfall_context'; @@ -25,9 +26,21 @@ const EmbeddableMock = ({ }) => (

{title}

-
{appendTitle}
+
+ {appendTitle} +
{reportType}
-
{JSON.stringify(attributes)}
+
+ {JSON.stringify(attributes)} +
); @@ -40,9 +53,8 @@ export const TestWrapper = ({ activeStep?: JourneyStep; children: JSX.Element; }) => ( - ), }, @@ -55,5 +67,5 @@ export const TestWrapper = ({ > {children} - + ); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx index 326e981d2c4e2..7aaeb39bf46e6 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/browser_test_results.tsx @@ -38,7 +38,7 @@ export const BrowserTestRunResult = ({ }); useEffect(() => { - const { core, toasts } = kibanaService; + const { coreStart, toasts } = kibanaService; if (retriesExceeded) { toasts.addDanger( { @@ -49,7 +49,7 @@ export const BrowserTestRunResult = ({ defaultMessage="Manual test run failed for {name}" values={{ name }} />, - core + coreStart ), }, { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx index f88b6b3483a43..ba9d4ff87566c 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/test_now_mode/manual_test_run_mode/simple_test_results.tsx @@ -25,7 +25,7 @@ export function SimpleTestResults({ name, testRunId, expectPings, onDone }: Prop useEffect(() => { if (retriesExceeded) { - const { core, toasts } = kibanaService; + const { coreStart, toasts } = kibanaService; toasts.addDanger( { @@ -36,7 +36,7 @@ export function SimpleTestResults({ name, testRunId, expectPings, onDone }: Prop defaultMessage="Manual test run failed for {name}" values={{ name }} />, - core + coreStart ), }, { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/index.ts index e3171165a8639..662d6337d5a0e 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/index.ts @@ -7,5 +7,3 @@ export * from './synthetics_refresh_context'; export * from './synthetics_settings_context'; -export * from './synthetics_theme_context'; -export * from './synthetics_startup_plugins_context'; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_embeddable_context.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_embeddable_context.tsx new file mode 100644 index 0000000000000..0ae588714bf97 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_embeddable_context.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 React, { createContext, useContext, useMemo } from 'react'; +import { History, createMemoryHistory } from 'history'; + +interface SyntheticsEmbeddableContext { + history: History; +} + +const defaultContext: SyntheticsEmbeddableContext = {} as SyntheticsEmbeddableContext; + +export const SyntheticsEmbeddableContext = createContext(defaultContext); + +export const SyntheticsEmbeddableStateContextProvider: React.FC = ({ children }) => { + const value = useMemo(() => { + return { history: createMemoryHistory() }; + }, []); + + return ; +}; + +export const useSyntheticsEmbeddableContext = () => useContext(SyntheticsEmbeddableContext); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx index b221997efe9c3..14799683d96d5 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx @@ -27,13 +27,13 @@ export interface CommonlyUsedDateRange { export interface SyntheticsAppProps { basePath: string; canSave: boolean; - core: CoreStart; + coreStart: CoreStart; darkMode: boolean; i18n: I18nStart; isApmAvailable: boolean; isInfraAvailable: boolean; isLogsAvailable: boolean; - plugins: ClientPluginsSetup; + setupPlugins: ClientPluginsSetup; startPlugins: ClientPluginsStart; setBadge: (badge?: ChromeBadge) => void; renderGlobalHelpControls(): void; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_shared_context.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_shared_context.tsx new file mode 100644 index 0000000000000..948bf538c2faf --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_shared_context.tsx @@ -0,0 +1,63 @@ +/* + * 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 { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { Provider as ReduxProvider } from 'react-redux'; +import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { SyntheticsRefreshContextProvider } from './synthetics_refresh_context'; +import { SyntheticsDataViewContextProvider } from './synthetics_data_view_context'; +import { SyntheticsAppProps } from './synthetics_settings_context'; +import { storage, store } from '../state'; + +export const SyntheticsSharedContext: React.FC = ({ + coreStart, + setupPlugins, + startPlugins, + children, + darkMode, +}) => { + return ( + + + + + + + {children} + + + + + + + ); +}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_startup_plugins_context.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_startup_plugins_context.tsx deleted file mode 100644 index cfb28cbbff553..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_startup_plugins_context.tsx +++ /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 React, { createContext, useContext, PropsWithChildren } from 'react'; -import { ClientPluginsStart } from '../../../plugin'; - -export const SyntheticsStartupPluginsContext = createContext>({}); - -export const SyntheticsStartupPluginsContextProvider: React.FC< - PropsWithChildren> -> = ({ children, ...props }) => ( - -); - -export const useSyntheticsStartPlugins = () => useContext(SyntheticsStartupPluginsContext); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_theme_context.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_theme_context.tsx deleted file mode 100644 index b57bdaa0cd365..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/contexts/synthetics_theme_context.tsx +++ /dev/null @@ -1,97 +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 { euiLightVars, euiDarkVars } from '@kbn/ui-theme'; -import React, { createContext, useContext, useMemo, FC, PropsWithChildren } from 'react'; -import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; - -export interface SyntheticsAppColors { - danger: string; - dangerBehindText: string; - success: string; - gray: string; - range: string; - mean: string; - warning: string; - lightestShade: string; -} - -export interface SyntheticsThemeContextValues { - colors: SyntheticsAppColors; - chartTheme: { - baseTheme?: Theme; - theme?: PartialTheme; - }; -} - -/** - * These are default values for the context. These defaults are typically - * overwritten by the Synthetics App upon its invocation. - */ -const defaultContext: SyntheticsThemeContextValues = { - colors: { - danger: euiLightVars.euiColorDanger, - dangerBehindText: euiDarkVars.euiColorVis9_behindText, - mean: euiLightVars.euiColorPrimary, - range: euiLightVars.euiFocusBackgroundColor, - success: euiLightVars.euiColorSuccess, - warning: euiLightVars.euiColorWarning, - gray: euiLightVars.euiColorLightShade, - lightestShade: euiLightVars.euiColorLightestShade, - }, - chartTheme: { - baseTheme: LIGHT_THEME, - }, -}; - -export const SyntheticsThemeContext = createContext(defaultContext); - -interface ThemeContextProps { - darkMode: boolean; -} - -export const SyntheticsThemeContextProvider: FC> = ({ - darkMode, - children, -}) => { - let colors: SyntheticsAppColors; - if (darkMode) { - colors = { - danger: euiDarkVars.euiColorVis9, - dangerBehindText: euiDarkVars.euiColorVis9_behindText, - mean: euiDarkVars.euiColorPrimary, - gray: euiDarkVars.euiColorLightShade, - range: euiDarkVars.euiFocusBackgroundColor, - success: euiDarkVars.euiColorSuccess, - warning: euiDarkVars.euiColorWarning, - lightestShade: euiDarkVars.euiColorLightestShade, - }; - } else { - colors = { - danger: euiLightVars.euiColorVis9, - dangerBehindText: euiLightVars.euiColorVis9_behindText, - mean: euiLightVars.euiColorPrimary, - gray: euiLightVars.euiColorLightShade, - range: euiLightVars.euiFocusBackgroundColor, - success: euiLightVars.euiColorSuccess, - warning: euiLightVars.euiColorWarning, - lightestShade: euiLightVars.euiColorLightestShade, - }; - } - const value = useMemo(() => { - return { - colors, - chartTheme: { - baseTheme: darkMode ? DARK_THEME : LIGHT_THEME, - }, - }; - }, [colors, darkMode]); - - return ; -}; - -export const useSyntheticsThemeContext = () => useContext(SyntheticsThemeContext); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_edit_monitor_locator.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_edit_monitor_locator.ts index 034ca4ec6807a..a0ecb681e38c2 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_edit_monitor_locator.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_edit_monitor_locator.ts @@ -8,7 +8,8 @@ import { useEffect, useState } from 'react'; import { LocatorClient } from '@kbn/share-plugin/common/url_service/locators'; import { syntheticsEditMonitorLocatorID } from '@kbn/observability-plugin/common'; -import { useSyntheticsStartPlugins } from '../contexts'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ClientPluginsStart } from '../../../plugin'; export function useEditMonitorLocator({ configId, @@ -18,7 +19,7 @@ export function useEditMonitorLocator({ locators?: LocatorClient; }) { const [editUrl, setEditUrl] = useState(undefined); - const syntheticsLocators = useSyntheticsStartPlugins()?.share?.url.locators; + const syntheticsLocators = useKibana().services.share?.url.locators; const locator = (locators || syntheticsLocators)?.get(syntheticsEditMonitorLocatorID); useEffect(() => { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_monitor_detail_locator.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_monitor_detail_locator.ts index d1c181da28f17..fc346bccfa6c6 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_monitor_detail_locator.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/hooks/use_monitor_detail_locator.ts @@ -7,7 +7,8 @@ import { useEffect, useState } from 'react'; import { syntheticsMonitorDetailLocatorID } from '@kbn/observability-plugin/common'; -import { useSyntheticsStartPlugins } from '../contexts'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ClientPluginsStart } from '../../../plugin'; export function useMonitorDetailLocator({ configId, @@ -17,7 +18,7 @@ export function useMonitorDetailLocator({ locationId?: string; }) { const [monitorUrl, setMonitorUrl] = useState(undefined); - const locator = useSyntheticsStartPlugins()?.share?.url.locators.get( + const locator = useKibana().services?.share?.url.locators.get( syntheticsMonitorDetailLocatorID ); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/lazy_wrapper/monitor_status.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/lazy_wrapper/monitor_status.tsx index 7c33dc7aba96e..e0c843eca0bd4 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/lazy_wrapper/monitor_status.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/lazy_wrapper/monitor_status.tsx @@ -19,19 +19,19 @@ import { store } from '../../../state'; import type { StatusRuleParams } from '../../../../../../common/rules/status_rule'; interface Props { - core: CoreStart; + coreStart: CoreStart; plugins: ClientPluginsStart; params: RuleTypeParamsExpressionProps; } // eslint-disable-next-line import/no-default-export -export default function MonitorStatusAlert({ core, plugins, params }: Props) { - kibanaService.core = core; +export default function MonitorStatusAlert({ coreStart, plugins, params }: Props) { + kibanaService.coreStart = coreStart; const queryClient = new QueryClient(); return ( - + ['ruleParams']; setRuleParams: RuleTypeParamsExpressionProps['setRuleParams']; } // eslint-disable-next-line import/no-default-export -export default function TLSAlert({ core, plugins, ruleParams, setRuleParams }: Props) { - kibanaService.core = core; +export default function TLSAlert({ coreStart, plugins, ruleParams, setRuleParams }: Props) { + kibanaService.coreStart = coreStart; return ( - + diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx index 8ee01e185e8c1..ba86407859408 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx @@ -33,7 +33,7 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({ return `${docLinks.links.observability.syntheticsAlerting}`; }, ruleParamsExpression: (paramProps: RuleTypeParamsExpressionProps) => ( - + ), validate: (_ruleParams: StatusRuleParams) => { return { errors: {} }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/tls.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/tls.tsx index 2479d1f466f3e..15c0fa90ec605 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/tls.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/lib/alert_types/tls.tsx @@ -31,7 +31,7 @@ export const initTlsAlertType: AlertTypeInitializer = ({ }, ruleParamsExpression: (params: RuleTypeParamsExpressionProps) => ( { + const { isDev, isServerless, coreStart, startPlugins, setupPlugins, appMountParameters } = + kibanaService; + const { application: { capabilities }, chrome: { setBadge, setHelpExtension }, @@ -30,7 +26,7 @@ export function renderApp( http: { basePath }, i18n, theme, - } = core; + } = kibanaService.coreStart; const { apm, infrastructure, logs } = getIntegratedAppAvailability( capabilities, @@ -40,24 +36,22 @@ export function renderApp( const canSave = (capabilities.uptime.save ?? false) as boolean; // TODO: Determine for synthetics const darkMode = theme.getTheme().darkMode; - const props: SyntheticsAppProps = { + return { isDev, - plugins, + setupPlugins, canSave, - core, + coreStart, i18n, startPlugins, basePath: basePath.get(), darkMode, - commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES), + commonlyUsedRanges: coreStart.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES), isApmAvailable: apm, isInfraAvailable: infrastructure, isLogsAvailable: logs, renderGlobalHelpControls: () => setHelpExtension({ - appName: i18nFormatter.translate('xpack.synthetics.header.appName', { - defaultMessage: 'Synthetics', - }), + appName: SYNTHETICS_APP_NAME, links: [ { linkType: 'documentation', @@ -72,13 +66,21 @@ export function renderApp( setBadge, appMountParameters, isServerless, - setBreadcrumbs: startPlugins.serverless?.setBreadcrumbs ?? core.chrome.setBreadcrumbs, + setBreadcrumbs: startPlugins.serverless?.setBreadcrumbs ?? coreStart.chrome.setBreadcrumbs, }; +}; + +export function renderApp(appMountParameters: AppMountParameters) { + const props: SyntheticsAppProps = getSyntheticsAppProps(); ReactDOM.render(, appMountParameters.element); return () => { - startPlugins.data.search.session.clear(); + props.startPlugins.data.search.session.clear(); ReactDOM.unmountComponentAtNode(appMountParameters.element); }; } + +const SYNTHETICS_APP_NAME = i18nFormatter.translate('xpack.synthetics.header.appName', { + defaultMessage: 'Synthetics', +}); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx index 0083a5a75339f..04c5065014289 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/monitor_list/toast_title.tsx @@ -11,5 +11,5 @@ import { toMountPoint } from '@kbn/react-kibana-mount'; import { kibanaService } from '../../../../utils/kibana_service'; export function toastTitle({ title, testAttribute }: { title: string; testAttribute?: string }) { - return toMountPoint(

{title}

, kibanaService.core); + return toMountPoint(

{title}

, kibanaService.coreStart); } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/index.ts index 5125e723f13db..69b735302ee75 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/index.ts @@ -9,7 +9,11 @@ import { createReducer } from '@reduxjs/toolkit'; import { OverviewStatusState } from '../../../../../common/runtime_types'; import { IHttpSerializedFetchError } from '..'; -import { clearOverviewStatusErrorAction, fetchOverviewStatusAction } from './actions'; +import { + clearOverviewStatusErrorAction, + fetchOverviewStatusAction, + quietFetchOverviewStatusAction, +} from './actions'; export interface OverviewStatusStateReducer { loading: boolean; @@ -29,6 +33,10 @@ export const overviewStatusReducer = createReducer(initialState, (builder) => { builder .addCase(fetchOverviewStatusAction.get, (state) => { state.status = null; + state.loading = true; + }) + .addCase(quietFetchOverviewStatusAction.get, (state) => { + state.loading = true; }) .addCase(fetchOverviewStatusAction.success, (state, action) => { state.status = { @@ -36,9 +44,11 @@ export const overviewStatusReducer = createReducer(initialState, (builder) => { allConfigs: { ...action.payload.upConfigs, ...action.payload.downConfigs }, }; state.loaded = true; + state.loading = false; }) .addCase(fetchOverviewStatusAction.fail, (state, action) => { state.error = action.payload; + state.loading = false; }) .addCase(clearOverviewStatusErrorAction, (state) => { state.error = null; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/selectors.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/selectors.ts index 07745420b86c8..c23eed413d107 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/selectors.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/overview_status/selectors.ts @@ -8,5 +8,5 @@ import { SyntheticsAppState } from '../root_reducer'; export const selectOverviewStatus = ({ - overviewStatus: { status, error, loaded }, -}: SyntheticsAppState) => ({ status, error, loaded }); + overviewStatus: { status, error, loaded, loading }, +}: SyntheticsAppState) => ({ status, error, loaded, loading }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/settings/effects.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/settings/effects.ts index 551838a6f6ec4..fdc7c55a7a053 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/settings/effects.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/settings/effects.ts @@ -81,13 +81,13 @@ export function* setDynamicSettingsEffect() { yield call(setDynamicSettings, { settings: action.payload }); yield put(updateDefaultAlertingAction.get()); yield put(setDynamicSettingsAction.success(action.payload)); - kibanaService.core.notifications.toasts.addSuccess( + kibanaService.coreSetup.notifications.toasts.addSuccess( i18n.translate('xpack.synthetics.settings.saveSuccess', { defaultMessage: 'Settings saved!', }) ); } catch (err) { - kibanaService.core.notifications.toasts.addError(err, { + kibanaService.coreSetup.notifications.toasts.addError(err, { title: couldNotSaveSettingsText, }); yield put(setDynamicSettingsAction.fail(err)); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/utils/fetch_effect.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/utils/fetch_effect.ts index d9412849114ab..5883c55196ff7 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/utils/fetch_effect.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/utils/fetch_effect.ts @@ -67,7 +67,7 @@ export function fetchEffectFactory( if (typeof onFailure === 'function') { onFailure?.(error); } else if (typeof onFailure === 'string') { - kibanaService.core.notifications.toasts.addError( + kibanaService.coreSetup.notifications.toasts.addError( { ...error, message: serializedError.body?.message ?? error.message }, { title: onFailure, @@ -104,7 +104,7 @@ export function fetchEffectFactory( if (typeof onSuccess === 'function') { onSuccess(response as R); } else if (onSuccess && typeof onSuccess === 'string') { - kibanaService.core.notifications.toasts.addSuccess(onSuccess); + kibanaService.coreSetup.notifications.toasts.addSuccess(onSuccess); } } } catch (error) { diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx index 93c45442695ad..61cf6b69763da 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/synthetics_app.tsx @@ -6,42 +6,30 @@ */ import React, { useEffect } from 'react'; -import { Provider as ReduxProvider } from 'react-redux'; import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { InspectorContextProvider } from '@kbn/observability-shared-plugin/public'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; -import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Router } from '@kbn/shared-ux-router'; +import { SyntheticsSharedContext } from './contexts/synthetics_shared_context'; import { kibanaService } from '../../utils/kibana_service'; import { ActionMenu } from './components/common/header/action_menu'; import { TestNowModeFlyoutContainer } from './components/test_now_mode/test_now_mode_flyout_container'; -import { - SyntheticsAppProps, - SyntheticsRefreshContextProvider, - SyntheticsSettingsContextProvider, - SyntheticsStartupPluginsContextProvider, - SyntheticsThemeContextProvider, -} from './contexts'; -import { SyntheticsDataViewContextProvider } from './contexts/synthetics_data_view_context'; +import { SyntheticsAppProps, SyntheticsSettingsContextProvider } from './contexts'; import { PageRouter } from './routes'; -import { setBasePath, storage, store } from './state'; +import { setBasePath, store } from './state'; const Application = (props: SyntheticsAppProps) => { const { basePath, canSave, - core, - darkMode, - plugins, + coreStart, + startPlugins, renderGlobalHelpControls, setBadge, - startPlugins, appMountParameters, } = props; @@ -62,16 +50,17 @@ const Application = (props: SyntheticsAppProps) => { ); }, [canSave, renderGlobalHelpControls, setBadge]); - kibanaService.core = core; - kibanaService.startPlugins = startPlugins; kibanaService.theme = props.appMountParameters.theme$; store.dispatch(setBasePath(basePath)); + const PresentationContextProvider = + startPlugins.presentationUtil?.ContextProvider ?? React.Fragment; + return ( - + { }, }} > - - - - - - - - - -
- - - - - - - -
-
-
-
-
-
-
-
-
-
+ + + + +
+ + + + + +
+
+
+
+
); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx index 552229fe47346..9d4870b8c9154 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/rtl_helpers.tsx @@ -34,10 +34,7 @@ import { MountWithReduxProvider } from './helper_with_redux'; import { AppState } from '../../state'; import { stringifyUrlParams } from '../url_params'; import { ClientPluginsStart } from '../../../../plugin'; -import { - SyntheticsRefreshContextProvider, - SyntheticsStartupPluginsContextProvider, -} from '../../contexts'; +import { SyntheticsRefreshContextProvider } from '../../contexts'; import { kibanaService } from '../../../../utils/kibana_service'; type DeepPartial = { @@ -182,21 +179,14 @@ export function MockKibanaProvider({ }: MockKibanaProviderProps) { const coreOptions = merge({}, mockCore(), core); - kibanaService.core = coreOptions as any; + kibanaService.coreStart = coreOptions as any; return ( - - - {children} - - + + {children} + ); diff --git a/x-pack/plugins/observability_solution/synthetics/public/plugin.ts b/x-pack/plugins/observability_solution/synthetics/public/plugin.ts index 50c79681a4ebc..1192b4d991f55 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/plugin.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/plugin.ts @@ -25,7 +25,7 @@ import type { ExploratoryViewPublicSetup, ExploratoryViewPublicStart, } from '@kbn/exploratory-view-plugin/public'; -import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { EmbeddableStart, EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, @@ -50,12 +50,18 @@ import type { ObservabilitySharedPluginSetup, ObservabilitySharedPluginStart, } from '@kbn/observability-shared-plugin/public'; + import { LicenseManagementUIPluginSetup } from '@kbn/license-management-plugin/public/plugin'; import { ObservabilityAIAssistantPublicSetup, ObservabilityAIAssistantPublicStart, } from '@kbn/observability-ai-assistant-plugin/public'; import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; +import { DashboardStart, DashboardSetup } from '@kbn/dashboard-plugin/public'; +import { registerSyntheticsEmbeddables } from './apps/embeddables/register_embeddables'; +import { kibanaService } from './utils/kibana_service'; import { PLUGIN } from '../common/constants/plugin'; import { OVERVIEW_ROUTE } from '../common/constants/ui'; import { locators } from './apps/locators'; @@ -72,7 +78,10 @@ export interface ClientPluginsSetup { share: SharePluginSetup; triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; cloud?: CloudSetup; + embeddable: EmbeddableSetup; serverless?: ServerlessPluginSetup; + uiActions: UiActionsSetup; + dashboard: DashboardSetup; } export interface ClientPluginsStart { @@ -102,6 +111,8 @@ export interface ClientPluginsStart { usageCollection: UsageCollectionStart; serverless: ServerlessPluginStart; licenseManagement?: LicenseManagementUIPluginSetup; + presentationUtil: PresentationUtilPluginStart; + dashboard: DashboardStart; } export interface SyntheticsPluginServices extends Partial { @@ -123,12 +134,25 @@ export class SyntheticsPlugin this._packageInfo = initContext.env.packageInfo; } - public setup(core: CoreSetup, plugins: ClientPluginsSetup): void { + public setup( + coreSetup: CoreSetup, + plugins: ClientPluginsSetup + ): void { locators.forEach((locator) => { plugins.share.url.locators.create(locator); }); - registerSyntheticsRoutesWithNavigation(core, plugins); + registerSyntheticsRoutesWithNavigation(coreSetup, plugins); + + coreSetup.getStartServices().then(([coreStart, clientPluginsStart]) => { + kibanaService.init({ + coreSetup, + coreStart, + startPlugins: clientPluginsStart, + isDev: this.initContext.env.mode.dev, + isServerless: this._isServerless, + }); + }); const appKeywords = [ 'Synthetics', @@ -149,7 +173,7 @@ export class SyntheticsPlugin ]; // Register the Synthetics UI plugin - core.application.register({ + coreSetup.application.register({ id: 'synthetics', euiIconType: 'logoObservability', order: 8400, @@ -177,23 +201,20 @@ export class SyntheticsPlugin }, ], mount: async (params: AppMountParameters) => { - const [coreStart, corePlugins] = await core.getStartServices(); - + kibanaService.appMountParameters = params; const { renderApp } = await import('./apps/synthetics/render_app'); - return renderApp( - coreStart, - plugins, - corePlugins, - params, - this.initContext.env.mode.dev, - this._isServerless - ); + await coreSetup.getStartServices(); + + return renderApp(params); }, }); + + registerSyntheticsEmbeddables(coreSetup, plugins); } public start(coreStart: CoreStart, pluginsStart: ClientPluginsStart): void { const { triggersActionsUi } = pluginsStart; + setStartServices(coreStart); setStartServices(coreStart); diff --git a/x-pack/plugins/observability_solution/synthetics/public/utils/kibana_service/kibana_service.ts b/x-pack/plugins/observability_solution/synthetics/public/utils/kibana_service/kibana_service.ts index 021d8c7ec3d7d..292a0e058737b 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/utils/kibana_service/kibana_service.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/utils/kibana_service/kibana_service.ts @@ -6,43 +6,46 @@ */ import type { Observable } from 'rxjs'; -import type { CoreStart, CoreTheme } from '@kbn/core/public'; -import { ClientPluginsStart } from '../../plugin'; +import type { CoreStart, CoreTheme, CoreSetup } from '@kbn/core/public'; +import { AppMountParameters } from '@kbn/core/public'; +import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin'; import { apiService } from '../api_service/api_service'; class KibanaService { private static instance: KibanaService; - private _core!: CoreStart; - private _startPlugins!: ClientPluginsStart; - private _theme!: Observable; - - public get core() { - return this._core; - } - - public set core(coreStart: CoreStart) { - this._core = coreStart; - apiService.http = this._core.http; - } - - public get startPlugins() { - return this._startPlugins; - } - - public set startPlugins(startPlugins: ClientPluginsStart) { - this._startPlugins = startPlugins; - } - - public get theme() { - return this._theme; - } - - public set theme(coreTheme: Observable) { - this._theme = coreTheme; + public coreStart!: CoreStart; + public coreSetup!: CoreSetup; + public theme!: Observable; + public setupPlugins!: ClientPluginsSetup; + public isDev!: boolean; + public isServerless!: boolean; + public appMountParameters!: AppMountParameters; + public startPlugins!: ClientPluginsStart; + + public init({ + coreSetup, + coreStart, + startPlugins, + isDev, + isServerless, + }: { + coreSetup: CoreSetup; + coreStart: CoreStart; + startPlugins: ClientPluginsStart; + isDev: boolean; + isServerless: boolean; + }) { + this.coreSetup = coreSetup; + this.coreStart = coreStart; + this.startPlugins = startPlugins; + this.theme = coreStart.uiSettings.get$('theme:darkMode'); + apiService.http = coreStart.http; + this.isDev = isDev; + this.isServerless = isServerless; } public get toasts() { - return this._core.notifications.toasts; + return this.coreStart.notifications.toasts; } private constructor() {} diff --git a/x-pack/plugins/observability_solution/synthetics/tsconfig.json b/x-pack/plugins/observability_solution/synthetics/tsconfig.json index 95ed03ba36a11..f5df3a24c8ea1 100644 --- a/x-pack/plugins/observability_solution/synthetics/tsconfig.json +++ b/x-pack/plugins/observability_solution/synthetics/tsconfig.json @@ -40,7 +40,6 @@ "@kbn/kibana-react-plugin", "@kbn/i18n-react", "@kbn/securitysolution-io-ts-utils", - "@kbn/ui-theme", "@kbn/es-query", "@kbn/stack-connectors-plugin", "@kbn/rule-data-utils", @@ -90,7 +89,15 @@ "@kbn/react-kibana-mount", "@kbn/react-kibana-context-render", "@kbn/react-kibana-context-theme", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/core-lifecycle-browser", + "@kbn/ui-actions-browser", + "@kbn/presentation-publishing", + "@kbn/presentation-containers", + "@kbn/ui-actions-plugin", + "@kbn/presentation-util-plugin", + "@kbn/core-application-browser", + "@kbn/dashboard-plugin" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/osquery/common/api/asset/assets.gen.ts b/x-pack/plugins/osquery/common/api/asset/assets.gen.ts new file mode 100644 index 0000000000000..f0cc5209e13a4 --- /dev/null +++ b/x-pack/plugins/osquery/common/api/asset/assets.gen.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; 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: Assets Schema + * version: 1 + */ + +import { z } from 'zod'; + +import { AssetsRequestQuery } from './assets_status.gen'; + +export type ReadAssetsStatusRequestParams = z.infer; +export const ReadAssetsStatusRequestParams = z.object({ + query: AssetsRequestQuery, +}); +export type ReadAssetsStatusRequestParamsInput = z.input; + +export type ReadAssetsStatusResponse = z.infer; +export const ReadAssetsStatusResponse = z.object({}); + +export type UpdateAssetsStatusRequestParams = z.infer; +export const UpdateAssetsStatusRequestParams = z.object({ + query: AssetsRequestQuery, +}); +export type UpdateAssetsStatusRequestParamsInput = z.input; + +export type UpdateAssetsStatusResponse = z.infer; +export const UpdateAssetsStatusResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/asset/assets.schema.yaml b/x-pack/plugins/osquery/common/api/asset/assets.schema.yaml index 31688b7ce66cb..2769bc188ab20 100644 --- a/x-pack/plugins/osquery/common/api/asset/assets.schema.yaml +++ b/x-pack/plugins/osquery/common/api/asset/assets.schema.yaml @@ -5,25 +5,41 @@ info: paths: /internal/osquery/assets: get: + x-codegen-enabled: true + operationId: ReadAssetsStatus summary: Get assets parameters: - - $ref: './assets_status.schema.yaml#/components/parameters/AssetsStatusRequestQueryParameter' + - name: query + in: path + required: true + schema: + $ref: './assets_status.schema.yaml#/components/schemas/AssetsRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './assets_status.schema.yaml#/components/schemas/SuccessResponse' + type: object + properties: {} + # Define properties for the success response if needed /internal/osquery/assets/update: post: + x-codegen-enabled: true + operationId: UpdateAssetsStatus summary: Update assets parameters: - - $ref: './assets_status.schema.yaml#/components/parameters/AssetsStatusRequestQueryParameter' + - name: query + in: path + required: true + schema: + $ref: './assets_status.schema.yaml#/components/schemas/AssetsRequestQuery' responses: '200': description: OK content: application/json: schema: - $ref: './assets_status.schema.yaml#/components/schemas/SuccessResponse' + type: object + properties: {} + # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/asset/assets_status.gen.ts b/x-pack/plugins/osquery/common/api/asset/assets_status.gen.ts index 53a98b96612ea..fd3c50374943f 100644 --- a/x-pack/plugins/osquery/common/api/asset/assets_status.gen.ts +++ b/x-pack/plugins/osquery/common/api/asset/assets_status.gen.ts @@ -18,6 +18,3 @@ import { z } from 'zod'; export type AssetsRequestQuery = z.infer; export const AssetsRequestQuery = z.object({}); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/asset/assets_status.schema.yaml b/x-pack/plugins/osquery/common/api/asset/assets_status.schema.yaml index 48322c1266b07..fb57329a9992d 100644 --- a/x-pack/plugins/osquery/common/api/asset/assets_status.schema.yaml +++ b/x-pack/plugins/osquery/common/api/asset/assets_status.schema.yaml @@ -2,19 +2,8 @@ openapi: 3.0.0 info: title: Assets Status Schema version: '1' -paths: { } +paths: {} components: - parameters: - AssetsStatusRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/AssetsRequestQuery' schemas: AssetsRequestQuery: type: object - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/fleet_wrapper.gen.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/fleet_wrapper.gen.ts new file mode 100644 index 0000000000000..1ecea2c4caf19 --- /dev/null +++ b/x-pack/plugins/osquery/common/api/fleet_wrapper/fleet_wrapper.gen.ts @@ -0,0 +1,51 @@ +/* + * 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: Fleet wrapper schema + * version: 1 + */ + +import { z } from 'zod'; + +import { Id } from '../model/schema/common_attributes.gen'; + +export type GetAgentDetailsRequestParams = z.infer; +export const GetAgentDetailsRequestParams = z.object({ + id: Id, +}); +export type GetAgentDetailsRequestParamsInput = z.input; + +export type GetAgentDetailsResponse = z.infer; +export const GetAgentDetailsResponse = z.object({}); + +export type GetAgentPackagePoliciesResponse = z.infer; +export const GetAgentPackagePoliciesResponse = z.object({}); + +export type GetAgentPoliciesResponse = z.infer; +export const GetAgentPoliciesResponse = z.object({}); + +export type GetAgentPolicyRequestParams = z.infer; +export const GetAgentPolicyRequestParams = z.object({ + id: Id, +}); +export type GetAgentPolicyRequestParamsInput = z.input; + +export type GetAgentPolicyResponse = z.infer; +export const GetAgentPolicyResponse = z.object({}); +export type GetAgentsRequestQuery = z.infer; +export const GetAgentsRequestQuery = z.object({ + query: z.object({}), +}); +export type GetAgentsRequestQueryInput = z.input; + +export type GetAgentsResponse = z.infer; +export const GetAgentsResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/fleet_wrapper.schema.yaml b/x-pack/plugins/osquery/common/api/fleet_wrapper/fleet_wrapper.schema.yaml index 7e46e15abb825..fa5a576cb1a2e 100644 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/fleet_wrapper.schema.yaml +++ b/x-pack/plugins/osquery/common/api/fleet_wrapper/fleet_wrapper.schema.yaml @@ -5,66 +5,94 @@ info: paths: /internal/osquery/fleet_wrapper/agents: get: + x-codegen-enabled: true + operationId: GetAgents summary: Get agents parameters: - - $ref: './get_agents.schema.yaml#/components/parameters/GetAgentsRequestQueryParameter' + - name: query + in: query + required: true + schema: + type: object + additionalProperties: true responses: '200': description: OK content: application/json: schema: - $ref: './get_agents.schema.yaml#/components/schemas/SuccessResponse' + type: object + properties: {} + # Define properties for the success response if needed /internal/osquery/fleet_wrapper/agents/{id}: get: + x-codegen-enabled: true + operationId: GetAgentDetails summary: Get Agent details parameters: - - $ref: './get_agent_details.schema.yaml#/components/parameters/GetAgentDetailsRequestQueryParameter' + - name: id + in: path + required: true + schema: + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Id' responses: '200': description: OK content: application/json: schema: - $ref: './get_agent_details.schema.yaml#/components/schemas/SuccessResponse' + type: object + properties: {} + # Define properties for the success response if needed /internal/osquery/fleet_wrapper/agent_policies: get: + x-codegen-enabled: true + operationId: GetAgentPolicies summary: Get Agent policies - parameters: - - $ref: './get_agent_policies.schema.yaml#/components/parameters/GetAgentPoliciesRequestParameter' - - $ref: './get_agent_policies.schema.yaml#/components/parameters/GetAgentPoliciesRequestQueryParameter' responses: '200': description: OK content: application/json: schema: - $ref: './get_agent_policies.schema.yaml#/components/schemas/SuccessResponse' + type: object + properties: {} + # Define properties for the success response if needed /internal/osquery/fleet_wrapper/agent_policies/{id}: get: + x-codegen-enabled: true + operationId: GetAgentPolicy summary: Get Agent policy parameters: - - $ref: './get_agent_policy.schema.yaml#/components/parameters/GetAgentPolicyRequestParameter' + - name: id + in: path + required: true + schema: + $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/Id' responses: '200': description: OK content: application/json: schema: - $ref: './get_agent_policy.schema.yaml#/components/schemas/SuccessResponse' + type: object + properties: {} + # Define properties for the success response if needed /internal/osquery/fleet_wrapper/package_policies: get: + x-codegen-enabled: true + operationId: GetAgentPackagePolicies summary: Get Agent policy - parameters: - - $ref: './get_package_policies.schema.yaml#/components/parameters/GetPackagePoliciesRequestQueryParameter' responses: '200': description: OK content: application/json: schema: - $ref: './get_package_policies.schema.yaml#/components/schemas/SuccessResponse' + type: object + properties: {} + # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details.gen.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details.gen.ts deleted file mode 100644 index 5d721a018205b..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details.gen.ts +++ /dev/null @@ -1,23 +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 agent details schema - * version: 1 - */ - -import { z } from 'zod'; - -export type GetAgentDetailsRequestParams = z.infer; -export const GetAgentDetailsRequestParams = z.object({}); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details.schema.yaml b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details.schema.yaml deleted file mode 100644 index bdf4cb3329cdf..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_details.schema.yaml +++ /dev/null @@ -1,20 +0,0 @@ -openapi: 3.0.0 -info: - title: Get agent details schema - version: '1' -paths: { } -components: - parameters: - GetAgentDetailsRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/GetAgentDetailsRequestParams' - schemas: - GetAgentDetailsRequestParams: - type: object - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies.gen.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies.gen.ts deleted file mode 100644 index 875c21a600e93..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies.gen.ts +++ /dev/null @@ -1,23 +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 agent policies schema - * version: 1 - */ - -import { z } from 'zod'; - -export type GetAgentPoliciesRequestParams = z.infer; -export const GetAgentPoliciesRequestParams = z.object({}); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies.schema.yaml b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies.schema.yaml deleted file mode 100644 index cdfb521712674..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies.schema.yaml +++ /dev/null @@ -1,26 +0,0 @@ -openapi: 3.0.0 -info: - title: Get agent policies schema - version: '1' -paths: { } -components: - parameters: - GetAgentPoliciesRequestQueryParameter: - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/GetAgentPoliciesRequestParams' - GetAgentPoliciesRequestParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/GetAgentPoliciesRequestParams' - schemas: - GetAgentPoliciesRequestParams: - type: object - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies_route.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies_route.ts deleted file mode 100644 index 84a68e5fbf4c7..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policies_route.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. - */ - -import * as t from 'io-ts'; - -export const getAgentPoliciesRequestParamsSchema = t.unknown; - -export type GetAgentPoliciesRequestParamsSchema = t.OutputOf< - typeof getAgentPoliciesRequestParamsSchema ->; - -export const getAgentPoliciesRequestQuerySchema = t.unknown; - -export type GetAgentPoliciesRequestQuerySchema = t.OutputOf< - typeof getAgentPoliciesRequestQuerySchema ->; diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policy.gen.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policy.gen.ts deleted file mode 100644 index 3f19e274761bd..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policy.gen.ts +++ /dev/null @@ -1,27 +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 agent policy schema - * version: 1 - */ - -import { z } from 'zod'; - -import { Id } from '../model/schema/common_attributes.gen'; - -export type GetAgentPolicyRequestParams = z.infer; -export const GetAgentPolicyRequestParams = z.object({ - id: Id.optional(), -}); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policy.schema.yaml b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policy.schema.yaml deleted file mode 100644 index dc4a2607bfc6b..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_policy.schema.yaml +++ /dev/null @@ -1,23 +0,0 @@ -openapi: 3.0.0 -info: - title: Get agent policy schema - version: '1' -paths: { } -components: - parameters: - GetAgentPolicyRequestParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/GetAgentPolicyRequestParams' - schemas: - GetAgentPolicyRequestParams: - type: object - properties: - id: - $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/fleet_wrapper/get_agent_status.gen.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_status.gen.ts index 80adc112312a7..041aac0bf2320 100644 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_status.gen.ts +++ b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_status.gen.ts @@ -26,6 +26,3 @@ export const GetAgentStatusRequestQueryParams = z.object({ kuery: KueryOrUndefined.optional(), policyId: Id.optional(), }); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_status.schema.yaml b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_status.schema.yaml index e10174bee2634..af2e9307b4c12 100644 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_status.schema.yaml +++ b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agent_status.schema.yaml @@ -2,21 +2,8 @@ openapi: 3.0.0 info: title: Get agent status schema version: '1' -paths: { } +paths: {} components: - parameters: - GetAgentStatusRequestQueryParameter: - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/GetAgentStatusRequestQueryParams' - GetAgentStatusRequestParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/GetAgentStatusRequestParams' schemas: GetAgentStatusRequestParams: type: object @@ -27,7 +14,3 @@ components: $ref: '../model/schema/common_attributes.schema.yaml#/components/schemas/KueryOrUndefined' policyId: $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/fleet_wrapper/get_agents.gen.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agents.gen.ts deleted file mode 100644 index b162bcbfd967b..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agents.gen.ts +++ /dev/null @@ -1,23 +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 agents schema - * version: 1 - */ - -import { z } from 'zod'; - -export type GetAgentsRequestParams = z.infer; -export const GetAgentsRequestParams = z.object({}); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agents.schema.yaml b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agents.schema.yaml deleted file mode 100644 index c1a387512c3d3..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_agents.schema.yaml +++ /dev/null @@ -1,20 +0,0 @@ -openapi: 3.0.0 -info: - title: Get agents schema - version: '1' -paths: { } -components: - parameters: - GetAgentsRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/GetAgentsRequestParams' - schemas: - GetAgentsRequestParams: - type: object - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_package_policies.gen.ts b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_package_policies.gen.ts deleted file mode 100644 index f4c3be37371ea..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_package_policies.gen.ts +++ /dev/null @@ -1,23 +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 package policies schema - * version: 1 - */ - -import { z } from 'zod'; - -export type GetPackagePoliciesRequestParams = z.infer; -export const GetPackagePoliciesRequestParams = z.object({}); - -export type SuccessResponse = z.infer; -export const SuccessResponse = z.object({}); diff --git a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_package_policies.schema.yaml b/x-pack/plugins/osquery/common/api/fleet_wrapper/get_package_policies.schema.yaml deleted file mode 100644 index 708867e8f7fa1..0000000000000 --- a/x-pack/plugins/osquery/common/api/fleet_wrapper/get_package_policies.schema.yaml +++ /dev/null @@ -1,20 +0,0 @@ -openapi: 3.0.0 -info: - title: Get package policies schema - version: '1' -paths: { } -components: - parameters: - GetPackagePoliciesRequestQueryParameter: - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/GetPackagePoliciesRequestParams' - schemas: - GetPackagePoliciesRequestParams: - type: object - SuccessResponse: - type: object - properties: {} - # Define properties for the success response if needed diff --git a/x-pack/plugins/osquery/common/api/index.ts b/x-pack/plugins/osquery/common/api/index.ts index 681eaab583ca8..b1c42a8dc45e6 100644 --- a/x-pack/plugins/osquery/common/api/index.ts +++ b/x-pack/plugins/osquery/common/api/index.ts @@ -7,8 +7,7 @@ export * from './asset/get_assets_status_route'; export * from './asset/update_assets_status_route'; -export * from './fleet_wrapper/get_agent_policies_route'; -export * from './fleet_wrapper/get_agent_details_route'; +export * from './fleet_wrapper/fleet_wrapper.gen'; export * from './fleet_wrapper/get_agent_policy_route'; export * from './fleet_wrapper/get_agent_status_for_agent_policy_route'; export * from './fleet_wrapper/get_agents_route'; diff --git a/x-pack/plugins/osquery/common/api/status/privileges_check.schema.yaml b/x-pack/plugins/osquery/common/api/status/privileges_check.schema.yaml index 2702d1bafa040..8a8267a83f336 100644 --- a/x-pack/plugins/osquery/common/api/status/privileges_check.schema.yaml +++ b/x-pack/plugins/osquery/common/api/status/privileges_check.schema.yaml @@ -5,6 +5,7 @@ info: paths: /internal/osquery/privileges_check: get: + operationId: ReadPrivilegesCheck summary: Get Osquery privileges check responses: '200': @@ -13,4 +14,4 @@ paths: application/json: schema: type: object - properties: { } + properties: {} diff --git a/x-pack/plugins/osquery/common/api/status/status.schema.yaml b/x-pack/plugins/osquery/common/api/status/status.schema.yaml index 9ab4d3bd0e607..1ed1e096ba10e 100644 --- a/x-pack/plugins/osquery/common/api/status/status.schema.yaml +++ b/x-pack/plugins/osquery/common/api/status/status.schema.yaml @@ -5,6 +5,7 @@ info: paths: /internal/osquery/status: get: + operationId: ReadInstallationStatus summary: Get Osquery installation status responses: '200': @@ -13,4 +14,4 @@ paths: application/json: schema: type: object - properties: { } + properties: {} diff --git a/x-pack/plugins/osquery/scripts/openapi/generate.js b/x-pack/plugins/osquery/scripts/openapi/generate.js index 35c099301e81c..018a965702c3e 100644 --- a/x-pack/plugins/osquery/scripts/openapi/generate.js +++ b/x-pack/plugins/osquery/scripts/openapi/generate.js @@ -17,6 +17,4 @@ generate({ rootDir: OSQUERY_ROOT, sourceGlob: './**/*.schema.yaml', templateName: 'zod_operation_schema', - // TODO: Fix lint errors - skipLinting: true, }); diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts index b3b6539f9fc35..c1d445fd40183 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts @@ -6,12 +6,11 @@ */ import type { IRouter } from '@kbn/core/server'; -import type { GetAgentDetailsRequestParamsSchema } from '../../../common/api'; -import { buildRouteValidation } from '../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; -import { getAgentDetailsRequestParamsSchema } from '../../../common/api'; +import { GetAgentDetailsRequestParams } from '../../../common/api'; export const getAgentDetailsRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.versioned @@ -25,10 +24,7 @@ export const getAgentDetailsRoute = (router: IRouter, osqueryContext: OsqueryApp version: API_VERSIONS.internal.v1, validate: { request: { - params: buildRouteValidation< - typeof getAgentDetailsRequestParamsSchema, - GetAgentDetailsRequestParamsSchema - >(getAgentDetailsRequestParamsSchema), + params: buildRouteValidationWithZod(GetAgentDetailsRequestParams), }, }, }, @@ -38,8 +34,7 @@ export const getAgentDetailsRoute = (router: IRouter, osqueryContext: OsqueryApp try { agent = await osqueryContext.service .getAgentService() - ?.asInternalUser // @ts-expect-error update types - ?.getAgent(request.params.id); + ?.asInternalUser?.getAgent(request.params.id); } catch (err) { return response.notFound(); } diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts index ee80758652706..9e84410712506 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts @@ -11,19 +11,10 @@ import { satisfies } from 'semver'; import type { GetAgentPoliciesResponseItem, PackagePolicy } from '@kbn/fleet-plugin/common'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; -import type { - GetAgentPoliciesRequestParamsSchema, - GetAgentPoliciesRequestQuerySchema, -} from '../../../common/api'; -import { buildRouteValidation } from '../../utils/build_validation/route_validation'; import { API_VERSIONS } from '../../../common/constants'; import { OSQUERY_INTEGRATION_NAME, PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { getInternalSavedObjectsClient } from '../utils'; -import { - getAgentPoliciesRequestParamsSchema, - getAgentPoliciesRequestQuerySchema, -} from '../../../common/api'; export const getAgentPoliciesRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.versioned @@ -35,18 +26,7 @@ export const getAgentPoliciesRoute = (router: IRouter, osqueryContext: OsqueryAp .addVersion( { version: API_VERSIONS.internal.v1, - validate: { - request: { - params: buildRouteValidation< - typeof getAgentPoliciesRequestParamsSchema, - GetAgentPoliciesRequestParamsSchema - >(getAgentPoliciesRequestParamsSchema), - query: buildRouteValidation< - typeof getAgentPoliciesRequestQuerySchema, - GetAgentPoliciesRequestQuerySchema - >(getAgentPoliciesRequestQuerySchema), - }, - }, + validate: {}, }, async (context, request, response) => { const internalSavedObjectsClient = await getInternalSavedObjectsClient( diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts index 85de68f7e44d9..bad5b01289d52 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts @@ -6,13 +6,12 @@ */ import type { IRouter } from '@kbn/core/server'; -import type { GetAgentPolicyRequestParamsSchema } from '../../../common/api'; -import { buildRouteValidation } from '../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { getInternalSavedObjectsClient } from '../utils'; -import { getAgentPolicyRequestParamsSchema } from '../../../common/api'; +import { GetAgentPolicyRequestParams } from '../../../common/api'; export const getAgentPolicyRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.versioned @@ -26,10 +25,7 @@ export const getAgentPolicyRoute = (router: IRouter, osqueryContext: OsqueryAppC version: API_VERSIONS.internal.v1, validate: { request: { - params: buildRouteValidation< - typeof getAgentPolicyRequestParamsSchema, - GetAgentPolicyRequestParamsSchema - >(getAgentPolicyRequestParamsSchema), + params: buildRouteValidationWithZod(GetAgentPolicyRequestParams), }, }, }, diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts index 887fa4811e73e..86719125b97eb 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts @@ -7,13 +7,10 @@ import type { IRouter } from '@kbn/core/server'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import type { GetPackagePoliciesRequestQuerySchema } from '../../../common/api'; -import { buildRouteValidation } from '../../utils/build_validation/route_validation'; import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { getInternalSavedObjectsClient } from '../utils'; -import { getPackagePoliciesRequestQuerySchema } from '../../../common/api'; export const getPackagePoliciesRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.versioned @@ -25,14 +22,7 @@ export const getPackagePoliciesRoute = (router: IRouter, osqueryContext: Osquery .addVersion( { version: API_VERSIONS.internal.v1, - validate: { - request: { - query: buildRouteValidation< - typeof getPackagePoliciesRequestQuerySchema, - GetPackagePoliciesRequestQuerySchema - >(getPackagePoliciesRequestQuerySchema), - }, - }, + validate: {}, }, async (context, request, response) => { const internalSavedObjectsClient = await getInternalSavedObjectsClient( diff --git a/x-pack/plugins/osquery/tsconfig.json b/x-pack/plugins/osquery/tsconfig.json index 6d713311c777d..6cc74e9733a92 100644 --- a/x-pack/plugins/osquery/tsconfig.json +++ b/x-pack/plugins/osquery/tsconfig.json @@ -3,9 +3,7 @@ "compilerOptions": { "outDir": "target/types" }, - "exclude": [ - "target/**/*" - ], + "exclude": ["target/**/*"], "include": [ // add all the folders contains files to be compiled "common/**/*", @@ -77,6 +75,7 @@ "@kbn/openapi-generator", "@kbn/code-editor", "@kbn/search-types", - "@kbn/react-kibana-context-render" + "@kbn/react-kibana-context-render", + "@kbn/zod-helpers" ] } diff --git a/x-pack/plugins/search_playground/public/components/summarization_panel/instructions_field.tsx b/x-pack/plugins/search_playground/public/components/summarization_panel/instructions_field.tsx index 5db2dcc6c6d86..0bf870202f1e9 100644 --- a/x-pack/plugins/search_playground/public/components/summarization_panel/instructions_field.tsx +++ b/x-pack/plugins/search_playground/public/components/summarization_panel/instructions_field.tsx @@ -56,6 +56,7 @@ export const InstructionsField: React.FC = ({ value, onC fullWidth > { it('renders correctly with models', () => { const models = [ { + id: 'model1', name: 'Model1', disabled: false, icon: MockIcon, @@ -47,6 +48,7 @@ describe('SummarizationModel', () => { connectorType: LLMs.openai_azure, }, { + id: 'model2', name: 'Model2', disabled: true, icon: MockIcon, 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 996d963eaeb16..b31512177d3cb 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 @@ -23,9 +23,17 @@ let formHookSpy: jest.SpyInstance; import { useSourceIndicesFields } from './use_source_indices_field'; import { IndicesQuerySourceFields } from '../types'; -describe('useSourceIndicesFields Hook', () => { +// Failing: See https://github.com/elastic/kibana/issues/188840 +describe.skip('useSourceIndicesFields Hook', () => { let postMock: jest.Mock; + beforeEach(() => { + // Playground Provider has the formProvider which + // persists the form state into local storage + // We need to clear the local storage before each test + localStorage.clear(); + }); + const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); diff --git a/x-pack/plugins/search_playground/public/providers/form_provider.test.tsx b/x-pack/plugins/search_playground/public/providers/form_provider.test.tsx index 9ae9f4faac99b..73def5031615e 100644 --- a/x-pack/plugins/search_playground/public/providers/form_provider.test.tsx +++ b/x-pack/plugins/search_playground/public/providers/form_provider.test.tsx @@ -6,13 +6,14 @@ */ import React from 'react'; -import { render, waitFor } from '@testing-library/react'; +import { render, waitFor, act } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import { FormProvider } from './form_provider'; +import { FormProvider, LOCAL_STORAGE_KEY } from './form_provider'; import { useLoadFieldsByIndices } from '../hooks/use_load_fields_by_indices'; import { useLLMsModels } from '../hooks/use_llms_models'; import * as ReactHookForm from 'react-hook-form'; import { ChatFormFields } from '../types'; +import { useSearchParams } from 'react-router-dom-v5-compat'; jest.mock('../hooks/use_load_fields_by_indices'); jest.mock('../hooks/use_llms_models'); @@ -24,6 +25,21 @@ let formHookSpy: jest.SpyInstance; const mockUseLoadFieldsByIndices = useLoadFieldsByIndices as jest.Mock; const mockUseLLMsModels = useLLMsModels as jest.Mock; +const mockUseSearchParams = useSearchParams as jest.Mock; + +const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: (key: string) => store[key] || null, + setItem: (key: string, value: string) => { + store[key] = value; + }, + clear: () => { + store = {}; + }, + }; +})() as Storage; describe('FormProvider', () => { beforeEach(() => { @@ -34,11 +50,12 @@ describe('FormProvider', () => { afterEach(() => { jest.clearAllMocks(); + localStorageMock.clear(); }); it('renders the form provider with initial values, no default model', async () => { render( - +
Test Child Component
); @@ -65,7 +82,7 @@ describe('FormProvider', () => { mockUseLLMsModels.mockReturnValueOnce(mockModels); render( - +
Test Child Component
); @@ -88,14 +105,139 @@ describe('FormProvider', () => { mockUseLLMsModels.mockReturnValueOnce(modelsWithAllDisabled); render( - +
Test Child Component
); await waitFor(() => { expect(mockUseLoadFieldsByIndices).toHaveBeenCalled(); - expect(modelsWithAllDisabled.find((model) => !model.disabled)).toBeUndefined(); + }); + + expect( + formHookSpy.mock.results[0].value.getValues(ChatFormFields.summarizationModel) + ).toBeUndefined(); + }); + + it('saves form state to localStorage', async () => { + render( + +
Test Child Component
+
+ ); + + const { setValue } = formHookSpy.mock.results[0].value; + + act(() => { + setValue(ChatFormFields.prompt, 'New prompt'); + }); + + await waitFor(() => { + expect(localStorageMock.getItem(LOCAL_STORAGE_KEY)).toEqual( + JSON.stringify({ + prompt: 'New prompt', + doc_size: 3, + source_fields: {}, + indices: [], + summarization_model: undefined, + }) + ); + }); + }); + + it('loads form state from localStorage', async () => { + localStorageMock.setItem( + LOCAL_STORAGE_KEY, + JSON.stringify({ + prompt: 'Loaded prompt', + doc_size: 3, + source_fields: {}, + indices: [], + summarization_model: undefined, + }) + ); + + render( + +
Test Child Component
+
+ ); + + const { getValues } = formHookSpy.mock.results[0].value; + + await waitFor(() => { + expect(getValues()).toEqual({ + prompt: 'Loaded prompt', + doc_size: 3, + source_fields: {}, + indices: [], + summarization_model: undefined, + }); + }); + }); + + it('overrides the session model to the default when not found in list', async () => { + const mockModels = [ + { id: 'model1', name: 'Model 1', disabled: false }, + { id: 'model2', name: 'Model 2', disabled: true }, + ]; + + mockUseLLMsModels.mockReturnValueOnce(mockModels); + + localStorageMock.setItem( + LOCAL_STORAGE_KEY, + JSON.stringify({ + prompt: 'Loaded prompt', + doc_size: 3, + source_fields: {}, + indices: [], + summarization_model: { id: 'non-exist-model', name: 'Model 1', disabled: false }, + }) + ); + + render( + +
Test Child Component
+
+ ); + + const { getValues } = formHookSpy.mock.results[0].value; + + await waitFor(() => { + expect(getValues().summarization_model).toEqual({ + id: 'model1', + name: 'Model 1', + disabled: false, + }); + }); + }); + + it('updates indices from search params', async () => { + const mockSearchParams = new URLSearchParams(); + mockSearchParams.get = jest.fn().mockReturnValue('new-index'); + mockUseSearchParams.mockReturnValue([mockSearchParams]); + + localStorageMock.setItem( + LOCAL_STORAGE_KEY, + JSON.stringify({ + prompt: 'Loaded prompt', + doc_size: 3, + source_fields: {}, + indices: ['old-index'], + summarization_model: undefined, + }) + ); + + render( + +
Test Child Component
+
+ ); + + const { getValues } = formHookSpy.mock.results[0].value; + + await waitFor(() => { + expect(getValues(ChatFormFields.indices)).toEqual(['new-index']); }); }); }); diff --git a/x-pack/plugins/search_playground/public/providers/form_provider.tsx b/x-pack/plugins/search_playground/public/providers/form_provider.tsx index a319d15f63d20..03c0ce5652e19 100644 --- a/x-pack/plugins/search_playground/public/providers/form_provider.tsx +++ b/x-pack/plugins/search_playground/public/providers/form_provider.tsx @@ -11,25 +11,61 @@ import { useLoadFieldsByIndices } from '../hooks/use_load_fields_by_indices'; import { ChatForm, ChatFormFields } from '../types'; import { useLLMsModels } from '../hooks/use_llms_models'; -export const FormProvider: React.FC = ({ children }) => { +type PartialChatForm = Partial; +export const LOCAL_STORAGE_KEY = 'search_playground_session'; + +const DEFAULT_FORM_VALUES: PartialChatForm = { + prompt: 'You are an assistant for question-answering tasks.', + doc_size: 3, + source_fields: {}, + indices: [], + summarization_model: undefined, +}; + +const getLocalSession = (storage: Storage): PartialChatForm => { + try { + const localSessionJSON = storage.getItem(LOCAL_STORAGE_KEY); + const sessionState = localSessionJSON ? JSON.parse(localSessionJSON) : {}; + + return { + ...DEFAULT_FORM_VALUES, + ...sessionState, + }; + } catch (e) { + return DEFAULT_FORM_VALUES; + } +}; + +const setLocalSession = (state: PartialChatForm, storage: Storage) => { + storage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(state)); +}; + +interface FormProviderProps { + storage?: Storage; +} + +export const FormProvider: React.FC = ({ children, storage = localStorage }) => { const models = useLLMsModels(); const [searchParams] = useSearchParams(); const index = useMemo(() => searchParams.get('default-index'), [searchParams]); + const sessionState = useMemo(() => getLocalSession(storage), [storage]); const form = useForm({ - defaultValues: { - prompt: 'You are an assistant for question-answering tasks.', - doc_size: 3, - source_fields: {}, - indices: index ? [index] : [], - summarization_model: undefined, - }, + defaultValues: { ...sessionState, indices: index ? [index] : sessionState.indices }, }); useLoadFieldsByIndices({ watch: form.watch, setValue: form.setValue, getValues: form.getValues }); + useEffect(() => { + const subscription = form.watch((values) => + setLocalSession(values as Partial, storage) + ); + return () => subscription.unsubscribe(); + }, [form, storage]); + useEffect(() => { const defaultModel = models.find((model) => !model.disabled); + const currentModel = form.getValues(ChatFormFields.summarizationModel); - if (defaultModel && !form.getValues(ChatFormFields.summarizationModel)) { + if (defaultModel && (!currentModel || !models.find((model) => currentModel.id === model.id))) { form.setValue(ChatFormFields.summarizationModel, defaultModel); } }, [form, models]); diff --git a/x-pack/plugins/search_playground/public/types.ts b/x-pack/plugins/search_playground/public/types.ts index f44e354d411db..2bbd45ff16230 100644 --- a/x-pack/plugins/search_playground/public/types.ts +++ b/x-pack/plugins/search_playground/public/types.ts @@ -73,7 +73,7 @@ export interface ChatForm { [ChatFormFields.citations]: boolean; [ChatFormFields.indices]: string[]; [ChatFormFields.summarizationModel]: LLMModel; - [ChatFormFields.elasticsearchQuery]: { retriever: unknown }; // RetrieverContainer leads to "Type instantiation is excessively deep and possibly infinite" error + [ChatFormFields.elasticsearchQuery]: { retriever: any }; // RetrieverContainer leads to "Type instantiation is excessively deep and possibly infinite" error [ChatFormFields.sourceFields]: { [index: string]: string[] }; [ChatFormFields.docSize]: number; [ChatFormFields.queryFields]: { [index: string]: string[] }; @@ -203,6 +203,7 @@ export interface UseChatHelpers { } export interface LLMModel { + id: string; name: string; value?: string; showConnectorName?: boolean; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/index_management/create_index/create_index.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/index_management/create_index/create_index.schema.yaml index 63213117bd9fb..b825f5f7af7c0 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/index_management/create_index/create_index.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/index_management/create_index/create_index.schema.yaml @@ -35,6 +35,7 @@ paths: schema: $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 404: + description: Not found content: application/json: schema: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml index 4c38c57da7592..ddfbf564de2ac 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml @@ -38,6 +38,7 @@ paths: schema: $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' 404: + description: Not found content: application/json: schema: 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 deleted file mode 100644 index ba07c49a7b130..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema_legacy/common_attributes.ts +++ /dev/null @@ -1,60 +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 { 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; - -/** - * NOTE: Never make this a strict uuid, we allow the rule_id to be any string at the moment - * in case we encounter 3rd party rule systems which might be using auto incrementing numbers - * or other different things. - */ -export type RuleSignatureId = t.TypeOf; -export const RuleSignatureId = t.string; // should be non-empty string? - -export type RuleName = t.TypeOf; -export const RuleName = NonEmptyString; - -/** - * Outcome is a property of the saved object resolve api - * will tell us info about the rule after 8.0 migrations - */ -export type SavedObjectResolveOutcome = t.TypeOf; -export const SavedObjectResolveOutcome = t.union([ - t.literal('exactMatch'), - t.literal('aliasMatch'), - t.literal('conflict'), -]); - -export type SavedObjectResolveAliasTargetId = t.TypeOf; -export const SavedObjectResolveAliasTargetId = t.string; - -export type SavedObjectResolveAliasPurpose = t.TypeOf; -export const SavedObjectResolveAliasPurpose = t.union([ - t.literal('savedObjectConversion'), - t.literal('savedObjectImport'), -]); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_exceptions/find_exception_references/find_exception_references_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_exceptions/find_exception_references/find_exception_references_route.ts index cbef9a41de718..63b9363bb97c4 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_exceptions/find_exception_references/find_exception_references_route.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_exceptions/find_exception_references/find_exception_references_route.ts @@ -12,10 +12,17 @@ import { list_id, DefaultNamespaceArray, } from '@kbn/securitysolution-io-ts-list-types'; -import { NonEmptyStringArray } from '@kbn/securitysolution-io-ts-types'; +import { NonEmptyStringArray, NonEmptyString, UUID } from '@kbn/securitysolution-io-ts-types'; + // TODO https://github.com/elastic/security-team/issues/7491 -// eslint-disable-next-line no-restricted-imports -import { RuleName, RuleObjectId, RuleSignatureId } from '../../model/rule_schema_legacy'; +type RuleObjectId = t.TypeOf; +const RuleObjectId = UUID; + +type RuleSignatureId = t.TypeOf; +const RuleSignatureId = t.string; + +type RuleName = t.TypeOf; +const RuleName = NonEmptyString; // If ids and list_ids are undefined, route will fetch all lists matching the // specified namespace type diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.gen.ts index c0d00e394b6b1..5315edc16ab9f 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.gen.ts @@ -18,7 +18,35 @@ import { z } from 'zod'; import { CreateAssetCriticalityRecord } from './common.gen'; -export type AssetCriticalityBulkUploadRequest = z.infer; -export const AssetCriticalityBulkUploadRequest = z.object({ +export type AssetCriticalityBulkUploadErrorItem = z.infer< + typeof AssetCriticalityBulkUploadErrorItem +>; +export const AssetCriticalityBulkUploadErrorItem = z.object({ + message: z.string(), + index: z.number().int(), +}); + +export type AssetCriticalityBulkUploadStats = z.infer; +export const AssetCriticalityBulkUploadStats = z.object({ + successful: z.number().int(), + failed: z.number().int(), + total: z.number().int(), +}); + +export type BulkUpsertAssetCriticalityRecordsRequestBody = z.infer< + typeof BulkUpsertAssetCriticalityRecordsRequestBody +>; +export const BulkUpsertAssetCriticalityRecordsRequestBody = z.object({ records: z.array(CreateAssetCriticalityRecord).min(1).max(1000), }); +export type BulkUpsertAssetCriticalityRecordsRequestBodyInput = z.input< + typeof BulkUpsertAssetCriticalityRecordsRequestBody +>; + +export type BulkUpsertAssetCriticalityRecordsResponse = z.infer< + typeof BulkUpsertAssetCriticalityRecordsResponse +>; +export const BulkUpsertAssetCriticalityRecordsResponse = z.object({ + errors: z.array(AssetCriticalityBulkUploadErrorItem), + stats: AssetCriticalityBulkUploadStats, +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.schema.yaml index b4b7d5d2f1fe4..c0fecede6da72 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.schema.yaml @@ -13,40 +13,82 @@ paths: /api/asset_criticality/bulk: post: x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: BulkUpsertAssetCriticalityRecords summary: Bulk upsert asset criticality data, creating or updating records as needed requestBody: content: application/json: schema: - $ref: '#/components/schemas/AssetCriticalityBulkUploadRequest' - + type: object + example: + records: + - id_value: 'host-1' + id_field: 'host.name' + criticality_level: 'low_impact' + - id_value: 'host-2' + id_field: 'host.name' + criticality_level: 'medium_impact' + properties: + records: + type: array + minItems: 1 + maxItems: 1000 + items: + $ref: './common.schema.yaml#/components/schemas/CreateAssetCriticalityRecord' + required: + - records responses: '200': description: Bulk upload successful content: application/json: schema: - $ref: './common.schema.yaml#/components/schemas/AssetCriticalityBulkUploadResponse' + type: object + example: + errors: + - message: 'Invalid ID field' + index: 0 + stats: + successful: 1 + failed: 1 + total: 2 + properties: + errors: + type: array + items: + $ref: '#/components/schemas/AssetCriticalityBulkUploadErrorItem' + stats: + $ref: '#/components/schemas/AssetCriticalityBulkUploadStats' + required: + - errors + - stats '413': description: File too large + components: schemas: - AssetCriticalityBulkUploadRequest: + AssetCriticalityBulkUploadErrorItem: type: object - example: - records: - - id_value: 'host-1' - id_field: 'host.name' - criticality_level: 'low_impact' - - id_value: 'host-2' - id_field: 'host.name' - criticality_level: 'medium_impact' properties: - records: - type: array - minItems: 1 - maxItems: 1000 - items: - $ref: './common.schema.yaml#/components/schemas/CreateAssetCriticalityRecord' + message: + type: string + index: + type: integer required: - - records + - message + - index + + AssetCriticalityBulkUploadStats: + type: object + properties: + successful: + type: integer + failed: + type: integer + total: + type: integer + required: + - successful + - failed + - total diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts index 4b689d22944e1..dfaa5d852c993 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts @@ -53,28 +53,6 @@ export const CreateAssetCriticalityRecord = AssetCriticalityRecordIdParts.merge( }) ); -export type CreateSingleAssetCriticalityRequest = z.infer< - typeof CreateSingleAssetCriticalityRequest ->; -export const CreateSingleAssetCriticalityRequest = CreateAssetCriticalityRecord.merge( - z.object({ - /** - * If 'wait_for' the request will wait for the index refresh. - */ - refresh: z.literal('wait_for').optional(), - }) -); - -export type DeleteAssetCriticalityRecord = z.infer; -export const DeleteAssetCriticalityRecord = AssetCriticalityRecordIdParts.merge( - z.object({ - /** - * If 'wait_for' the request will wait for the index refresh. - */ - refresh: z.literal('wait_for').optional(), - }) -); - export type AssetCriticalityRecord = z.infer; export const AssetCriticalityRecord = CreateAssetCriticalityRecord.merge( z.object({ @@ -84,24 +62,3 @@ export const AssetCriticalityRecord = CreateAssetCriticalityRecord.merge( '@timestamp': z.string().datetime(), }) ); - -export type AssetCriticalityBulkUploadErrorItem = z.infer< - typeof AssetCriticalityBulkUploadErrorItem ->; -export const AssetCriticalityBulkUploadErrorItem = z.object({ - message: z.string(), - index: z.number().int(), -}); - -export type AssetCriticalityBulkUploadStats = z.infer; -export const AssetCriticalityBulkUploadStats = z.object({ - successful: z.number().int(), - failed: z.number().int(), - total: z.number().int(), -}); - -export type AssetCriticalityBulkUploadResponse = z.infer; -export const AssetCriticalityBulkUploadResponse = z.object({ - errors: z.array(AssetCriticalityBulkUploadErrorItem), - stats: AssetCriticalityBulkUploadStats, -}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml index 3218ec07e0fe2..8d3e05ab59bac 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml @@ -58,24 +58,6 @@ components: $ref: '#/components/schemas/AssetCriticalityLevel' required: - criticality_level - CreateSingleAssetCriticalityRequest: - allOf: - - $ref: '#/components/schemas/CreateAssetCriticalityRecord' - - type: object - properties: - refresh: - type: string - enum: [wait_for] - description: If 'wait_for' the request will wait for the index refresh. - DeleteAssetCriticalityRecord: - allOf: - - $ref: '#/components/schemas/AssetCriticalityRecordIdParts' - - type: object - properties: - refresh: - type: string - enum: [wait_for] - description: If 'wait_for' the request will wait for the index refresh. AssetCriticalityRecord: allOf: - $ref: '#/components/schemas/CreateAssetCriticalityRecord' @@ -88,46 +70,3 @@ components: description: The time the record was created or updated. required: - '@timestamp' - AssetCriticalityBulkUploadErrorItem: - type: object - properties: - message: - type: string - index: - type: integer - required: - - message - - index - AssetCriticalityBulkUploadStats: - type: object - properties: - successful: - type: integer - failed: - type: integer - total: - type: integer - required: - - successful - - failed - - total - AssetCriticalityBulkUploadResponse: - type: object - example: - errors: - - message: 'Invalid ID field' - index: 0 - stats: - successful: 1 - failed: 1 - total: 2 - properties: - errors: - type: array - items: - $ref: '#/components/schemas/AssetCriticalityBulkUploadErrorItem' - stats: - $ref: '#/components/schemas/AssetCriticalityBulkUploadStats' - required: - - errors - - stats diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.gen.ts new file mode 100644 index 0000000000000..4836f4fe844dd --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.gen.ts @@ -0,0 +1,59 @@ +/* + * 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: Asset Criticality Create Record Schema + * version: 1 + */ + +import { z } from 'zod'; + +import { CreateAssetCriticalityRecord, AssetCriticalityRecord } from './common.gen'; + +export type CreateAssetCriticalityRecordRequestBody = z.infer< + typeof CreateAssetCriticalityRecordRequestBody +>; +export const CreateAssetCriticalityRecordRequestBody = CreateAssetCriticalityRecord.merge( + z.object({ + /** + * If 'wait_for' the request will wait for the index refresh. + */ + refresh: z.literal('wait_for').optional(), + }) +); +export type CreateAssetCriticalityRecordRequestBodyInput = z.input< + typeof CreateAssetCriticalityRecordRequestBody +>; + +export type CreateAssetCriticalityRecordResponse = z.infer< + typeof CreateAssetCriticalityRecordResponse +>; +export const CreateAssetCriticalityRecordResponse = AssetCriticalityRecord; + +export type InternalCreateAssetCriticalityRecordRequestBody = z.infer< + typeof InternalCreateAssetCriticalityRecordRequestBody +>; +export const InternalCreateAssetCriticalityRecordRequestBody = CreateAssetCriticalityRecord.merge( + z.object({ + /** + * If 'wait_for' the request will wait for the index refresh. + */ + refresh: z.literal('wait_for').optional(), + }) +); +export type InternalCreateAssetCriticalityRecordRequestBodyInput = z.input< + typeof InternalCreateAssetCriticalityRecordRequestBody +>; + +export type InternalCreateAssetCriticalityRecordResponse = z.infer< + typeof InternalCreateAssetCriticalityRecordResponse +>; +export const InternalCreateAssetCriticalityRecordResponse = AssetCriticalityRecord; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml index d59ce99c8717c..3d0bbf108d95f 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml @@ -14,14 +14,23 @@ paths: post: x-labels: [ess, serverless] x-internal: true - operationId: AssetCriticalityCreateRecord + x-codegen-enabled: true + operationId: InternalCreateAssetCriticalityRecord summary: Deprecated Internal Create Criticality Record + deprecated: true requestBody: required: true content: application/json: schema: - $ref: './common.schema.yaml#/components/schemas/CreateSingleAssetCriticalityRequest' + allOf: + - $ref: './common.schema.yaml#/components/schemas/CreateAssetCriticalityRecord' + - type: object + properties: + refresh: + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. responses: '200': description: Successful response @@ -34,14 +43,22 @@ paths: /api/asset_criticality: post: x-labels: [ess, serverless] - operationId: AssetCriticalityCreateRecord + x-codegen-enabled: true + operationId: CreateAssetCriticalityRecord summary: Create Criticality Record requestBody: required: true content: application/json: schema: - $ref: './common.schema.yaml#/components/schemas/CreateSingleAssetCriticalityRequest' + allOf: + - $ref: './common.schema.yaml#/components/schemas/CreateAssetCriticalityRecord' + - type: object + properties: + refresh: + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. responses: '200': description: Successful response diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen.ts new file mode 100644 index 0000000000000..fe290a67c6634 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Asset Criticality Delete Record Schema + * version: 1 + */ + +import { z } from 'zod'; + +import { IdField } from './common.gen'; + +export type DeleteAssetCriticalityRecordRequestQuery = z.infer< + typeof DeleteAssetCriticalityRecordRequestQuery +>; +export const DeleteAssetCriticalityRecordRequestQuery = z.object({ + /** + * The ID value of the asset. + */ + id_value: z.string(), + /** + * The field representing the ID. + */ + id_field: IdField, + /** + * If 'wait_for' the request will wait for the index refresh. + */ + refresh: z.literal('wait_for').optional(), +}); +export type DeleteAssetCriticalityRecordRequestQueryInput = z.input< + typeof DeleteAssetCriticalityRecordRequestQuery +>; + +export type InternalDeleteAssetCriticalityRecordRequestQuery = z.infer< + typeof InternalDeleteAssetCriticalityRecordRequestQuery +>; +export const InternalDeleteAssetCriticalityRecordRequestQuery = z.object({ + /** + * The ID value of the asset. + */ + id_value: z.string(), + /** + * The field representing the ID. + */ + id_field: IdField, + /** + * If 'wait_for' the request will wait for the index refresh. + */ + refresh: z.literal('wait_for').optional(), +}); +export type InternalDeleteAssetCriticalityRecordRequestQueryInput = z.input< + typeof InternalDeleteAssetCriticalityRecordRequestQuery +>; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml index 94e1cc82e15ad..d66a2283596c0 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml @@ -14,11 +14,31 @@ paths: delete: x-labels: [ess, serverless] x-internal: true - operationId: AssetCriticalityDeleteRecord + x-codegen-enabled: true + operationId: InternalDeleteAssetCriticalityRecord summary: Deprecated Internal Delete Criticality Record + deprecated: true parameters: - - $ref: './common.schema.yaml#/components/parameters/id_value' - - $ref: './common.schema.yaml#/components/parameters/id_field' + - name: id_value + in: query + required: true + schema: + type: string + description: The ID value of the asset. + - name: id_field + in: query + required: true + schema: + $ref: './common.schema.yaml#/components/schemas/IdField' + example: 'host.name' + description: The field representing the ID. + - name: refresh + in: query + required: false + schema: + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. responses: '200': description: Successful response @@ -27,11 +47,30 @@ paths: /api/asset_criticality: delete: x-labels: [ess, serverless] - operationId: AssetCriticalityDeleteRecord + x-codegen-enabled: true + operationId: DeleteAssetCriticalityRecord summary: Delete Criticality Record parameters: - - $ref: './common.schema.yaml#/components/parameters/id_value' - - $ref: './common.schema.yaml#/components/parameters/id_field' + - name: id_value + in: query + required: true + schema: + type: string + description: The ID value of the asset. + - name: id_field + in: query + required: true + schema: + $ref: './common.schema.yaml#/components/schemas/IdField' + example: 'host.name' + description: The field representing the ID. + - name: refresh + in: query + required: false + schema: + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. responses: '200': description: Successful response diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.gen.ts new file mode 100644 index 0000000000000..7437960ef9cae --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Asset Criticality Get Record Schema + * version: 1 + */ + +import { z } from 'zod'; + +import { IdField, AssetCriticalityRecord } from './common.gen'; + +export type GetAssetCriticalityRecordRequestQuery = z.infer< + typeof GetAssetCriticalityRecordRequestQuery +>; +export const GetAssetCriticalityRecordRequestQuery = z.object({ + /** + * The ID value of the asset. + */ + id_value: z.string(), + /** + * The field representing the ID. + */ + id_field: IdField, +}); +export type GetAssetCriticalityRecordRequestQueryInput = z.input< + typeof GetAssetCriticalityRecordRequestQuery +>; + +export type GetAssetCriticalityRecordResponse = z.infer; +export const GetAssetCriticalityRecordResponse = AssetCriticalityRecord; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml index 56f3e37de1126..ca2784c48653d 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml @@ -14,11 +14,23 @@ paths: get: x-labels: [ess, serverless] x-internal: true - operationId: AssetCriticalityGetRecord + operationId: InternalGetAssetCriticalityRecord summary: Deprecated Internal Get Criticality Record + deprecated: true parameters: - - $ref: './common.schema.yaml#/components/parameters/id_value' - - $ref: './common.schema.yaml#/components/parameters/id_field' + - name: id_value + in: query + required: true + schema: + type: string + description: The ID value of the asset. + - name: id_field + in: query + required: true + schema: + $ref: './common.schema.yaml#/components/schemas/IdField' + example: 'host.name' + description: The field representing the ID. responses: '200': description: Successful response @@ -33,11 +45,23 @@ paths: /api/asset_criticality: get: x-labels: [ess, serverless] - operationId: AssetCriticalityGetRecord + x-codegen-enabled: true + operationId: GetAssetCriticalityRecord summary: Get Criticality Record parameters: - - $ref: './common.schema.yaml#/components/parameters/id_value' - - $ref: './common.schema.yaml#/components/parameters/id_field' + - name: id_value + in: query + required: true + schema: + type: string + description: The ID value of the asset. + - name: id_field + in: query + required: true + schema: + $ref: './common.schema.yaml#/components/schemas/IdField' + example: 'host.name' + description: The field representing the ID. responses: '200': description: Successful response diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts index bb51693825def..f9d24b61bbef0 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts @@ -16,7 +16,7 @@ import { z } from 'zod'; -export type AssetCriticalityStatusResponse = z.infer; -export const AssetCriticalityStatusResponse = z.object({ +export type GetAssetCriticalityStatusResponse = z.infer; +export const GetAssetCriticalityStatusResponse = z.object({ asset_criticality_resources_installed: z.boolean().optional(), }); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml index 4052ad8f07177..f8f5dcb7c8ecd 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml @@ -14,7 +14,8 @@ paths: get: x-labels: [ess, serverless] x-internal: true - operationId: AssetCriticalityGetStatus + x-codegen-enabled: true + operationId: GetAssetCriticalityStatus summary: Get Asset Criticality Status responses: '200': @@ -22,14 +23,9 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AssetCriticalityStatusResponse' + type: object + properties: + asset_criticality_resources_installed: + type: boolean '400': description: Invalid request - -components: - schemas: - AssetCriticalityStatusResponse: - type: object - properties: - asset_criticality_resources_installed: - type: boolean diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts index 326a20d6c66a7..fb99a69f49f92 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts @@ -9,5 +9,5 @@ export * from './common.gen'; export * from './get_asset_criticality_status.gen'; export * from './get_asset_criticality_privileges.gen'; export * from './bulk_upload_asset_criticality.gen'; +export * from './upload_asset_criticality_csv.gen'; export * from './list_asset_criticality.gen'; -export * from './list_asset_criticality_query_params'; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.gen.ts index 9cf2f7ca7c628..e17a2b006896c 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.gen.ts @@ -18,8 +18,39 @@ import { z } from 'zod'; import { AssetCriticalityRecord } from './common.gen'; -export type AssetCriticalityListResponse = z.infer; -export const AssetCriticalityListResponse = z.object({ +export type FindAssetCriticalityRecordsRequestQuery = z.infer< + typeof FindAssetCriticalityRecordsRequestQuery +>; +export const FindAssetCriticalityRecordsRequestQuery = z.object({ + /** + * The field to sort by. + */ + sort_field: z.enum(['id_value', 'id_field', 'criticality_level', '@timestamp']).optional(), + /** + * The order to sort by. + */ + sort_direction: z.enum(['asc', 'desc']).optional(), + /** + * The page number to return. + */ + page: z.coerce.number().int().min(1).optional(), + /** + * The number of records to return per page. + */ + per_page: z.coerce.number().int().min(1).max(1000).optional(), + /** + * The kuery to filter by. + */ + kuery: z.string().optional(), +}); +export type FindAssetCriticalityRecordsRequestQueryInput = z.input< + typeof FindAssetCriticalityRecordsRequestQuery +>; + +export type FindAssetCriticalityRecordsResponse = z.infer< + typeof FindAssetCriticalityRecordsResponse +>; +export const FindAssetCriticalityRecordsResponse = z.object({ records: z.array(AssetCriticalityRecord), page: z.number().int().min(1), per_page: z.number().int().min(1).max(1000), diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.schema.yaml index 7c9a28c4eeaaf..34c5b98a4617f 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality.schema.yaml @@ -13,6 +13,8 @@ paths: /api/asset_criticality/list: post: x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: FindAssetCriticalityRecords summary: List asset criticality data, filtering and sorting as needed parameters: - name: sort_field @@ -26,7 +28,7 @@ paths: - criticality_level - \@timestamp description: The field to sort by. - - name: sort_order + - name: sort_direction in: query required: false schema: @@ -62,31 +64,24 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AssetCriticalityListResponse' - -components: - schemas: - AssetCriticalityListResponse: - type: object - properties: - records: - type: array - items: - $ref: './common.schema.yaml#/components/schemas/AssetCriticalityRecord' - page: - type: integer - minimum: 1 - per_page: - type: integer - minimum: 1 - maximum: 1000 - total: - type: integer - minimum: 0 - required: - - records - - page - - per_page - - total - - \ No newline at end of file + type: object + properties: + records: + type: array + items: + $ref: './common.schema.yaml#/components/schemas/AssetCriticalityRecord' + page: + type: integer + minimum: 1 + per_page: + type: integer + minimum: 1 + maximum: 1000 + total: + type: integer + minimum: 0 + required: + - records + - page + - per_page + - total diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality_query_params.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality_query_params.ts deleted file mode 100644 index b70393056c48f..0000000000000 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/list_asset_criticality_query_params.ts +++ /dev/null @@ -1,18 +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 { z } from 'zod'; - -export const ListAssetCriticalityQueryParams = z.object({ - page: z.coerce.number().min(1).optional(), - per_page: z.coerce.number().min(1).max(10000).optional(), - sort_field: z.enum(['id_field', 'id_value', '@timestamp', 'criticality_level']).optional(), - sort_direction: z.enum(['asc', 'desc']).optional(), - kuery: z.string().optional(), -}); - -export type ListAssetCriticalityQueryParams = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen.ts new file mode 100644 index 0000000000000..4282056378426 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen.ts @@ -0,0 +1,46 @@ +/* + * 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: Asset Criticality CSV Upload Schema + * version: 1 + */ + +import { z } from 'zod'; + +export type AssetCriticalityCsvUploadErrorItem = z.infer; +export const AssetCriticalityCsvUploadErrorItem = z.object({ + message: z.string(), + index: z.number().int(), +}); + +export type AssetCriticalityCsvUploadStats = z.infer; +export const AssetCriticalityCsvUploadStats = z.object({ + successful: z.number().int(), + failed: z.number().int(), + total: z.number().int(), +}); + +export type InternalUploadAssetCriticalityRecordsResponse = z.infer< + typeof InternalUploadAssetCriticalityRecordsResponse +>; +export const InternalUploadAssetCriticalityRecordsResponse = z.object({ + errors: z.array(AssetCriticalityCsvUploadErrorItem), + stats: AssetCriticalityCsvUploadStats, +}); + +export type UploadAssetCriticalityRecordsResponse = z.infer< + typeof UploadAssetCriticalityRecordsResponse +>; +export const UploadAssetCriticalityRecordsResponse = z.object({ + errors: z.array(AssetCriticalityCsvUploadErrorItem), + stats: AssetCriticalityCsvUploadStats, +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml index c348dcefa8b78..77e78f5c6d4d3 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml @@ -14,7 +14,10 @@ paths: post: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: InternalUploadAssetCriticalityRecords summary: Deprecated internal API which Uploads a CSV file containing asset criticality data + deprecated: true requestBody: content: multipart/form-data: @@ -33,13 +36,33 @@ paths: content: application/json: schema: - $ref: '#./common/components/schemas/AssetCriticalityBulkUploadResponse' + type: object + example: + errors: + - message: 'Invalid ID field' + index: 0 + stats: + successful: 1 + failed: 1 + total: 2 + properties: + errors: + type: array + items: + $ref: '#/components/schemas/AssetCriticalityCsvUploadErrorItem' + stats: + $ref: '#/components/schemas/AssetCriticalityCsvUploadStats' + required: + - errors + - stats '413': description: File too large /api/asset_criticality/upload_csv: post: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: UploadAssetCriticalityRecords summary: Uploads a CSV file containing asset criticality data requestBody: content: @@ -59,6 +82,51 @@ paths: content: application/json: schema: - $ref: '#./common/components/schemas/AssetCriticalityBulkUploadResponse' + type: object + example: + errors: + - message: 'Invalid ID field' + index: 0 + stats: + successful: 1 + failed: 1 + total: 2 + properties: + errors: + type: array + items: + $ref: '#/components/schemas/AssetCriticalityCsvUploadErrorItem' + stats: + $ref: '#/components/schemas/AssetCriticalityCsvUploadStats' + required: + - errors + - stats '413': description: File too large + +components: + schemas: + AssetCriticalityCsvUploadErrorItem: + type: object + properties: + message: + type: string + index: + type: integer + required: + - message + - index + + AssetCriticalityCsvUploadStats: + type: object + properties: + successful: + type: integer + failed: + type: integer + total: + type: integer + required: + - successful + - failed + - total diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts index 620620c95b888..b50eb00db6301 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts @@ -16,13 +16,13 @@ import { z } from 'zod'; -export type RiskEngineDisableResponse = z.infer; -export const RiskEngineDisableResponse = z.object({ - success: z.boolean().optional(), -}); - export type RiskEngineDisableErrorResponse = z.infer; export const RiskEngineDisableErrorResponse = z.object({ message: z.string(), full_error: z.string(), }); + +export type DisableRiskEngineResponse = z.infer; +export const DisableRiskEngineResponse = z.object({ + success: z.boolean().optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.schema.yaml index 33f35aa1bef1b..c491ec74e2a50 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.schema.yaml @@ -18,6 +18,8 @@ paths: post: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: DisableRiskEngine summary: Disable the Risk Engine requestBody: content: @@ -28,7 +30,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RiskEngineDisableResponse' + type: object + properties: + success: + type: boolean '400': description: Task manager is unavailable content: @@ -44,11 +49,6 @@ paths: components: schemas: - RiskEngineDisableResponse: - type: object - properties: - success: - type: boolean RiskEngineDisableErrorResponse: type: object required: diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts index cee1121b778ae..7bdbfd17449db 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts @@ -16,13 +16,13 @@ import { z } from 'zod'; -export type RiskEngineEnableResponse = z.infer; -export const RiskEngineEnableResponse = z.object({ - success: z.boolean().optional(), -}); - -export type RiskEngineEnableErrorResponse = z.infer; -export const RiskEngineEnableErrorResponse = z.object({ +export type EnableRiskEngineErrorResponse = z.infer; +export const EnableRiskEngineErrorResponse = z.object({ message: z.string(), full_error: z.string(), }); + +export type EnableRiskEngineResponse = z.infer; +export const EnableRiskEngineResponse = z.object({ + success: z.boolean().optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.schema.yaml index 5cfd5ffdd4fdf..6b2656bbb21b0 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.schema.yaml @@ -18,6 +18,8 @@ paths: post: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: EnableRiskEngine summary: Enable the Risk Engine requestBody: content: @@ -28,7 +30,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RiskEngineEnableResponse' + type: object + properties: + success: + type: boolean '400': description: Task manager is unavailable content: @@ -40,16 +45,11 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RiskEngineEnableErrorResponse' + $ref: '#/components/schemas/EnableRiskEngineErrorResponse' components: schemas: - RiskEngineEnableResponse: - type: object - properties: - success: - type: boolean - RiskEngineEnableErrorResponse: + EnableRiskEngineErrorResponse: type: object required: - message diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts index d973a435b9aec..f9d79cd8f96a6 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts @@ -16,8 +16,8 @@ import { z } from 'zod'; -export type RiskEngineInitResult = z.infer; -export const RiskEngineInitResult = z.object({ +export type InitRiskEngineResult = z.infer; +export const InitRiskEngineResult = z.object({ risk_engine_enabled: z.boolean(), risk_engine_resources_installed: z.boolean(), risk_engine_configuration_created: z.boolean(), @@ -25,13 +25,13 @@ export const RiskEngineInitResult = z.object({ errors: z.array(z.string()), }); -export type RiskEngineInitResponse = z.infer; -export const RiskEngineInitResponse = z.object({ - result: RiskEngineInitResult, -}); - -export type RiskEngineInitErrorResponse = z.infer; -export const RiskEngineInitErrorResponse = z.object({ +export type InitRiskEngineErrorResponse = z.infer; +export const InitRiskEngineErrorResponse = z.object({ message: z.string(), full_error: z.string(), }); + +export type InitRiskEngineResponse = z.infer; +export const InitRiskEngineResponse = z.object({ + result: InitRiskEngineResult, +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml index 498ac266a9aa0..d1d35f4a720c6 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml @@ -16,6 +16,8 @@ paths: post: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: InitRiskEngine summary: Initialize the Risk Engine description: Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine responses: @@ -24,7 +26,12 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RiskEngineInitResponse' + type: object + required: + - result + properties: + result: + $ref: '#/components/schemas/InitRiskEngineResult' '400': description: Task manager is unavailable content: @@ -36,11 +43,11 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RiskEngineInitErrorResponse' + $ref: '#/components/schemas/InitRiskEngineErrorResponse' components: schemas: - RiskEngineInitResult: + InitRiskEngineResult: type: object required: - risk_engine_enabled @@ -62,15 +69,7 @@ components: items: type: string - RiskEngineInitResponse: - type: object - required: - - result - properties: - result: - $ref: '#/components/schemas/RiskEngineInitResult' - - RiskEngineInitErrorResponse: + InitRiskEngineErrorResponse: type: object required: - message diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts index c8d10bd87d75e..e01edb31397a6 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts @@ -18,7 +18,7 @@ import { z } from 'zod'; import { DateRange } from '../common/common.gen'; -export type RiskEngineSettingsResponse = z.infer; -export const RiskEngineSettingsResponse = z.object({ +export type ReadRiskEngineSettingsResponse = z.infer; +export const ReadRiskEngineSettingsResponse = z.object({ range: DateRange.optional(), }); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml index 3622a9ff7c62b..a5cc6d6b44008 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml @@ -16,7 +16,8 @@ paths: get: x-labels: [ess, serverless] x-internal: true - operationId: RiskEngineSettingsGet + x-codegen-enabled: true + operationId: ReadRiskEngineSettings summary: Get the settings of the Risk Engine responses: '200': @@ -24,12 +25,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RiskEngineSettingsResponse' - -components: - schemas: - RiskEngineSettingsResponse: - type: object - properties: - range: - $ref: '../common/common.schema.yaml#/components/schemas/DateRange' + type: object + properties: + range: + $ref: '../common/common.schema.yaml#/components/schemas/DateRange' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts index 6a6e15d9c71a3..0d3fd0b9f0dd4 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts @@ -30,3 +30,6 @@ export const RiskEngineStatusResponse = z.object({ */ is_max_amount_of_risk_engines_reached: z.boolean(), }); + +export type GetRiskEngineStatusResponse = z.infer; +export const GetRiskEngineStatusResponse = RiskEngineStatusResponse; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.schema.yaml index 3f1cc33e94288..57f46b99f3a77 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.schema.yaml @@ -16,6 +16,8 @@ paths: get: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: GetRiskEngineStatus summary: Get the status of the Risk Engine description: Returns the status of both the legacy transform-based risk engine, as well as the new risk engine responses: diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts index c9b6c8cc47aa3..ebefbd772ed96 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts @@ -41,3 +41,29 @@ export const RiskScoresEntityCalculationResponse = z.object({ success: z.boolean(), score: EntityRiskScoreRecord.optional(), }); + +export type DeprecatedTriggerRiskScoreCalculationRequestBody = z.infer< + typeof DeprecatedTriggerRiskScoreCalculationRequestBody +>; +export const DeprecatedTriggerRiskScoreCalculationRequestBody = RiskScoresEntityCalculationRequest; +export type DeprecatedTriggerRiskScoreCalculationRequestBodyInput = z.input< + typeof DeprecatedTriggerRiskScoreCalculationRequestBody +>; + +export type DeprecatedTriggerRiskScoreCalculationResponse = z.infer< + typeof DeprecatedTriggerRiskScoreCalculationResponse +>; +export const DeprecatedTriggerRiskScoreCalculationResponse = RiskScoresEntityCalculationResponse; + +export type TriggerRiskScoreCalculationRequestBody = z.infer< + typeof TriggerRiskScoreCalculationRequestBody +>; +export const TriggerRiskScoreCalculationRequestBody = RiskScoresEntityCalculationRequest; +export type TriggerRiskScoreCalculationRequestBodyInput = z.input< + typeof TriggerRiskScoreCalculationRequestBody +>; + +export type TriggerRiskScoreCalculationResponse = z.infer< + typeof TriggerRiskScoreCalculationResponse +>; +export const TriggerRiskScoreCalculationResponse = RiskScoresEntityCalculationResponse; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml index bb94305254885..69be93f7ceb49 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml @@ -19,8 +19,11 @@ paths: post: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: DeprecatedTriggerRiskScoreCalculation summary: Deprecated Trigger calculation of Risk Scores for an entity. Moved to /internal/risk_score/calculation/entity description: Calculates and persists Risk Scores for an entity, returning the calculated risk score. + deprecated: true requestBody: description: The entity type and identifier content: @@ -41,6 +44,8 @@ paths: /internal/risk_score/calculation/entity: post: x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: TriggerRiskScoreCalculation summary: Trigger calculation of Risk Scores for an entity description: Calculates and persists Risk Scores for an entity, returning the calculated risk score. requestBody: diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts index fe0b90e5a2e7a..13515d239c81c 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts @@ -83,3 +83,10 @@ export const RiskScoresPreviewResponse = z.object({ user: z.array(EntityRiskScoreRecord).optional(), }), }); + +export type PreviewRiskScoreRequestBody = z.infer; +export const PreviewRiskScoreRequestBody = RiskScoresPreviewRequest; +export type PreviewRiskScoreRequestBodyInput = z.input; + +export type PreviewRiskScoreResponse = z.infer; +export const PreviewRiskScoreResponse = RiskScoresPreviewResponse; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.schema.yaml index a2ce9bcafd697..424ca98436768 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.schema.yaml @@ -16,6 +16,8 @@ paths: post: x-labels: [ess, serverless] x-internal: true + x-codegen-enabled: true + operationId: PreviewRiskScore summary: Preview the calculation of Risk Scores description: Calculates and returns a list of Risk Scores, sorted by identifier_type and risk score. requestBody: diff --git a/x-pack/plugins/security_solution/common/api/timeline/model/api.ts b/x-pack/plugins/security_solution/common/api/timeline/model/api.ts index 3e69bd14b646c..10b12aee32f2f 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/model/api.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/model/api.ts @@ -15,12 +15,31 @@ import { Direction } from '../../../search_strategy'; import type { PinnedEvent } from '../pinned_events/pinned_events_route'; import { PinnedEventRuntimeType } from '../pinned_events/pinned_events_route'; // TODO https://github.com/elastic/security-team/issues/7491 -// eslint-disable-next-line no-restricted-imports -import { - SavedObjectResolveAliasPurpose, - SavedObjectResolveAliasTargetId, - SavedObjectResolveOutcome, -} from '../../detection_engine/model/rule_schema_legacy'; + +/** + * Outcome is a property of the saved object resolve api + * will tell us info about the rule after 8.0 migrations + */ +export type SavedObjectResolveOutcome = runtimeTypes.TypeOf; +export const SavedObjectResolveOutcome = runtimeTypes.union([ + runtimeTypes.literal('exactMatch'), + runtimeTypes.literal('aliasMatch'), + runtimeTypes.literal('conflict'), +]); + +export type SavedObjectResolveAliasTargetId = runtimeTypes.TypeOf< + typeof SavedObjectResolveAliasTargetId +>; +export const SavedObjectResolveAliasTargetId = runtimeTypes.string; + +export type SavedObjectResolveAliasPurpose = runtimeTypes.TypeOf< + typeof SavedObjectResolveAliasPurpose +>; +export const SavedObjectResolveAliasPurpose = runtimeTypes.union([ + runtimeTypes.literal('savedObjectConversion'), + runtimeTypes.literal('savedObjectImport'), +]); + import { ErrorSchema } from './error_schema'; export const BareNoteSchema = runtimeTypes.intersection([ diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml index 6cb21c69c0492..4fd2ec1aed3b6 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml @@ -91,6 +91,7 @@ paths: application/json: schema: $ref: '#/components/schemas/SiemErrorResponse' + description: Not found '500': content: application/json: @@ -131,6 +132,7 @@ paths: application/json: schema: $ref: '#/components/schemas/SiemErrorResponse' + description: Not found '500': content: application/json: diff --git a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_alert_response_actions_support.ts b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_alert_response_actions_support.ts index e56c10d589f5f..a483a5c465b3f 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_alert_response_actions_support.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_alert_response_actions_support.ts @@ -9,6 +9,7 @@ import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import { useMemo } from 'react'; import { find, some } from 'lodash/fp'; import { i18n } from '@kbn/i18n'; +import { getHostPlatform } from '../../lib/endpoint/utils/get_host_platform'; import { getAlertDetailsFieldValue } from '../../lib/endpoint/utils/get_event_details_field_values'; import { isAgentTypeAndActionSupported } from '../../lib/endpoint'; import type { @@ -176,16 +177,8 @@ export const useAlertResponseActionsSupport = ( }, [eventData]); const platform = useMemo(() => { - // TODO:TC I couldn't find host.os.family in the example data, thus using host.os.type and host.os.platform which are present one at a time in different type of events - if (agentType === 'crowdstrike') { - return ( - getAlertDetailsFieldValue({ category: 'host', field: 'host.os.type' }, eventData) || - getAlertDetailsFieldValue({ category: 'host', field: 'host.os.platform' }, eventData) - ); - } - - return getAlertDetailsFieldValue({ category: 'host', field: 'host.os.type' }, eventData); - }, [agentType, eventData]); + return getHostPlatform(eventData ?? []); + }, [eventData]); const unsupportedReason = useMemo(() => { if (!doesHostSupportResponseActions) { diff --git a/x-pack/plugins/security_solution/public/common/lib/endpoint/utils/get_host_platform.test.ts b/x-pack/plugins/security_solution/public/common/lib/endpoint/utils/get_host_platform.test.ts new file mode 100644 index 0000000000000..61cc2053eb8fc --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/endpoint/utils/get_host_platform.test.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 { set } from 'lodash'; +import { getHostPlatform } from './get_host_platform'; +import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; + +describe('getHostPlatform() util', () => { + const buildEcsData = (data: Record) => { + const ecsData = {}; + + for (const [key, value] of Object.entries(data)) { + set(ecsData, `host.os.${key}`, value); + } + + return ecsData; + }; + + const buildEventDetails = (data: Record) => { + const eventDetails: TimelineEventsDetailsItem[] = []; + + for (const [key, value] of Object.entries(data)) { + eventDetails.push({ + category: 'host', + field: `host.os.${key}`, + values: [value], + originalValue: value, + isObjectArray: false, + }); + } + + return eventDetails; + }; + + it.each` + title | setupData | expectedResult + ${'ECS data with host.os.platform info'} | ${buildEcsData({ platform: 'windows' })} | ${'windows'} + ${'ECS data with host.os.type info'} | ${buildEcsData({ type: 'Linux' })} | ${'linux'} + ${'ECS data with host.os.name info'} | ${buildEcsData({ name: 'MACOS' })} | ${'macos'} + ${'ECS data with all os info'} | ${buildEcsData({ platform: 'macos', type: 'windows', name: 'linux' })} | ${'macos'} + ${'Event Details data with host.os.platform info'} | ${buildEventDetails({ platform: 'windows' })} | ${'windows'} + ${'Event Details data with host.os.type info'} | ${buildEventDetails({ type: 'Linux' })} | ${'linux'} + ${'Event Details data with host.os.name info'} | ${buildEventDetails({ name: 'MACOS' })} | ${'macos'} + ${'Event Details data with all os info'} | ${buildEventDetails({ platform: 'macos', type: 'windows', name: 'linux' })} | ${'macos'} + `(`should handle $title`, ({ setupData, expectedResult }) => { + expect(getHostPlatform(setupData)).toEqual(expectedResult); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/lib/endpoint/utils/get_host_platform.ts b/x-pack/plugins/security_solution/public/common/lib/endpoint/utils/get_host_platform.ts new file mode 100644 index 0000000000000..52df785cabff0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/endpoint/utils/get_host_platform.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 type { Ecs } from '@elastic/ecs'; +import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; +import type { MaybeImmutable } from '../../../../../common/endpoint/types'; +import { getAlertDetailsFieldValue } from './get_event_details_field_values'; +import type { Platform } from '../../../../management/components/endpoint_responder/components/header_info/platforms'; + +type EcsHostData = MaybeImmutable>; + +const isTimelineEventDetailsItems = ( + data: EcsHostData | TimelineEventsDetailsItem[] +): data is TimelineEventsDetailsItem[] => { + return Array.isArray(data); +}; + +/** + * Retrieve a host's platform type from either ECS data or Event Details list of items + * @param data + */ +export const getHostPlatform = (data: EcsHostData | TimelineEventsDetailsItem[]): Platform => { + let platform = ''; + + if (isTimelineEventDetailsItems(data)) { + platform = (getAlertDetailsFieldValue({ category: 'host', field: 'host.os.platform' }, data) || + getAlertDetailsFieldValue({ category: 'host', field: 'host.os.type' }, data) || + getAlertDetailsFieldValue({ category: 'host', field: 'host.os.name' }, data)) as Platform; + } else { + platform = + ((data.host?.os?.platform || data.host?.os?.type || data.host?.os?.name) as Platform) || ''; + } + + return platform.toLowerCase() as Platform; +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts index aa3b432533027..500c327d86b0c 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts @@ -6,10 +6,11 @@ */ import { useMemo } from 'react'; -import type { RiskEngineDisableResponse } from '../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; +import type { UploadAssetCriticalityRecordsResponse } from '../../../common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen'; +import type { DisableRiskEngineResponse } from '../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; import type { RiskEngineStatusResponse } from '../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; -import type { RiskEngineInitResponse } from '../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; -import type { RiskEngineEnableResponse } from '../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; +import type { InitRiskEngineResponse } from '../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; +import type { EnableRiskEngineResponse } from '../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; import type { RiskScoresPreviewRequest, RiskScoresPreviewResponse, @@ -18,7 +19,6 @@ import type { RiskScoresEntityCalculationRequest, RiskScoresEntityCalculationResponse, } from '../../../common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; -import type { AssetCriticalityBulkUploadResponse } from '../../../common/entity_analytics/asset_criticality/types'; import type { AssetCriticalityRecord, EntityAnalyticsPrivileges, @@ -39,9 +39,9 @@ import { RISK_SCORE_ENTITY_CALCULATION_URL, API_VERSIONS, } from '../../../common/constants'; -import type { RiskEngineSettingsResponse } from '../../../common/api/entity_analytics/risk_engine'; import type { SnakeToCamelCase } from '../common/utils'; import { useKibana } from '../../common/lib/kibana/kibana_react'; +import type { ReadRiskEngineSettingsResponse } from '../../../common/api/entity_analytics/risk_engine'; export interface DeleteAssetCriticalityResponse { deleted: true; @@ -81,7 +81,7 @@ export const useEntityAnalyticsRoutes = () => { * Init risk score engine */ const initRiskEngine = () => - http.fetch(RISK_ENGINE_INIT_URL, { + http.fetch(RISK_ENGINE_INIT_URL, { version: '1', method: 'POST', }); @@ -90,7 +90,7 @@ export const useEntityAnalyticsRoutes = () => { * Enable risk score engine */ const enableRiskEngine = () => - http.fetch(RISK_ENGINE_ENABLE_URL, { + http.fetch(RISK_ENGINE_ENABLE_URL, { version: '1', method: 'POST', }); @@ -99,7 +99,7 @@ export const useEntityAnalyticsRoutes = () => { * Disable risk score engine */ const disableRiskEngine = () => - http.fetch(RISK_ENGINE_DISABLE_URL, { + http.fetch(RISK_ENGINE_DISABLE_URL, { version: '1', method: 'POST', }); @@ -181,12 +181,12 @@ export const useEntityAnalyticsRoutes = () => { const uploadAssetCriticalityFile = async ( fileContent: string, fileName: string - ): Promise => { + ): Promise => { const file = new File([new Blob([fileContent])], fileName, { type: 'text/csv' }); const body = new FormData(); body.append('file', file); - return http.fetch( + return http.fetch( ASSET_CRITICALITY_PUBLIC_CSV_UPLOAD_URL, { version: API_VERSIONS.public.v1, @@ -224,7 +224,7 @@ export const useEntityAnalyticsRoutes = () => { * Fetches risk engine settings */ const fetchRiskEngineSettings = () => - http.fetch(RISK_ENGINE_SETTINGS_URL, { + http.fetch(RISK_ENGINE_SETTINGS_URL, { version: '1', method: 'GET', }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts index e19cf94fc379f..fb8a0bbb12972 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts @@ -9,7 +9,7 @@ import { useMutation } from '@tanstack/react-query'; import type { TaskManagerUnavailableResponse } from '../../../../common/api/entity_analytics/common'; import type { RiskEngineDisableErrorResponse, - RiskEngineDisableResponse, + DisableRiskEngineResponse, } from '../../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; @@ -21,7 +21,7 @@ export const useDisableRiskEngineMutation = (options?: UseMutationOptions<{}>) = const { disableRiskEngine } = useEntityAnalyticsRoutes(); return useMutation< - RiskEngineDisableResponse, + DisableRiskEngineResponse, { body: RiskEngineDisableErrorResponse | TaskManagerUnavailableResponse } >(() => disableRiskEngine(), { ...options, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts index 658c4a5cdb185..cd5083d13892e 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts @@ -8,8 +8,8 @@ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; import type { TaskManagerUnavailableResponse } from '../../../../common/api/entity_analytics/common'; import type { - RiskEngineEnableErrorResponse, - RiskEngineEnableResponse, + EnableRiskEngineErrorResponse, + EnableRiskEngineResponse, } from '../../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; @@ -19,8 +19,8 @@ export const useEnableRiskEngineMutation = (options?: UseMutationOptions<{}>) => const invalidateRiskEngineStatusQuery = useInvalidateRiskEngineStatusQuery(); const { enableRiskEngine } = useEntityAnalyticsRoutes(); return useMutation< - RiskEngineEnableResponse, - { body: RiskEngineEnableErrorResponse | TaskManagerUnavailableResponse } + EnableRiskEngineResponse, + { body: EnableRiskEngineErrorResponse | TaskManagerUnavailableResponse } >(enableRiskEngine, { ...options, mutationKey: ENABLE_RISK_ENGINE_MUTATION_KEY, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts index 67d94257e9165..d774853c7d026 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts @@ -6,11 +6,11 @@ */ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; -import type { TaskManagerUnavailableResponse } from '../../../../common/api/entity_analytics/common'; import type { - RiskEngineInitErrorResponse, - RiskEngineInitResponse, + InitRiskEngineErrorResponse, + InitRiskEngineResponse, } from '../../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; +import type { TaskManagerUnavailableResponse } from '../../../../common/api/entity_analytics/common'; import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; @@ -21,8 +21,8 @@ export const useInitRiskEngineMutation = (options?: UseMutationOptions<{}>) => { const { initRiskEngine } = useEntityAnalyticsRoutes(); return useMutation< - RiskEngineInitResponse, - { body: RiskEngineInitErrorResponse | TaskManagerUnavailableResponse } + InitRiskEngineResponse, + { body: InitRiskEngineErrorResponse | TaskManagerUnavailableResponse } >(() => initRiskEngine(), { ...options, mutationKey: INIT_RISK_ENGINE_STATUS_KEY, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/result_step.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/result_step.tsx index 1652c85eace1f..c3be648103d7f 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/result_step.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/result_step.tsx @@ -18,11 +18,11 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; -import type { AssetCriticalityBulkUploadResponse } from '../../../../../common/entity_analytics/asset_criticality/types'; +import type { BulkUpsertAssetCriticalityRecordsResponse } from '../../../../../common/entity_analytics/asset_criticality/types'; import { buildAnnotationsFromError } from '../helpers'; export const AssetCriticalityResultStep: React.FC<{ - result?: AssetCriticalityBulkUploadResponse; + result?: BulkUpsertAssetCriticalityRecordsResponse; validLinesAsText: string; errorMessage?: string; onReturn: () => void; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.test.ts index 60b6191a777d6..3fa2eb89e5d65 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AssetCriticalityBulkUploadResponse } from '../../../../common/api/entity_analytics'; +import type { UploadAssetCriticalityRecordsResponse } from '../../../../common/api/entity_analytics'; import type { ReducerAction, ReducerState, ValidationStepState } from './reducer'; import { reducer } from './reducer'; import { FileUploaderSteps } from './types'; @@ -43,7 +43,7 @@ describe('reducer', () => { }); it('should handle "fileUploaded" action with response', () => { - const response: AssetCriticalityBulkUploadResponse = { + const response: UploadAssetCriticalityRecordsResponse = { errors: [], stats: { total: 10, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.ts index e7f233015434f..eb0153d261871 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/reducer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AssetCriticalityBulkUploadResponse } from '../../../../common/entity_analytics/asset_criticality/types'; +import type { UploadAssetCriticalityRecordsResponse } from '../../../../common/api/entity_analytics'; import { FileUploaderSteps } from './types'; import type { ValidatedFile } from './types'; import { isFilePickerStep, isValidationStep } from './helpers'; @@ -26,7 +26,7 @@ export interface ValidationStepState { export interface ResultStepState { step: FileUploaderSteps.RESULT; - fileUploadResponse?: AssetCriticalityBulkUploadResponse; + fileUploadResponse?: UploadAssetCriticalityRecordsResponse; fileUploadError?: string; validLinesAsText: string; } @@ -46,7 +46,7 @@ export type ReducerAction = | { type: 'uploadingFile' } | { type: 'fileUploaded'; - payload: { response?: AssetCriticalityBulkUploadResponse; errorMessage?: string }; + payload: { response?: UploadAssetCriticalityRecordsResponse; errorMessage?: string }; }; export const INITIAL_STATE: FilePickerState = { diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_info/endpoint/header_endpoint_info.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_info/endpoint/header_endpoint_info.tsx index f302a31c5f48e..0cd96b4f3acf0 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_info/endpoint/header_endpoint_info.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_info/endpoint/header_endpoint_info.tsx @@ -7,6 +7,7 @@ import React, { memo } from 'react'; import { EuiSkeletonText } from '@elastic/eui'; +import { getHostPlatform } from '../../../../../../common/lib/endpoint/utils/get_host_platform'; import { AgentStatus } from '../../../../../../common/components/endpoint/agents/agent_status'; import { HeaderAgentInfo } from '../header_agent_info'; import { useGetEndpointDetails } from '../../../../../hooks'; @@ -31,7 +32,7 @@ export const HeaderEndpointInfo = memo(({ endpointId }) return ( { const HEARTBEAT_COUNT = 2001; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index abda617e2a779..9773e8f7eafb6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -8,7 +8,6 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { pagePathGetters } from '@kbn/fleet-plugin/public'; -import type { Platform } from '../../../../components/endpoint_responder/components/header_info/platforms'; import type { EndpointCapabilities } from '../../../../../../common/endpoint/service/response_actions/constants'; import { useUserPrivileges } from '../../../../../common/components/user_privileges'; import { useWithShowResponder } from '../../../../hooks'; @@ -20,6 +19,7 @@ import { agentPolicies, uiQueryParams } from '../../store/selectors'; import { useAppUrl } from '../../../../../common/lib/kibana/hooks'; import type { ContextMenuItemNavByRouterProps } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router'; import { isEndpointHostIsolated } from '../../../../../common/utils/validators'; +import { getHostPlatform } from '../../../../../common/lib/endpoint/utils/get_host_platform'; interface Options { isEndpointList: boolean; @@ -131,7 +131,7 @@ export const useEndpointActionItems = ( capabilities: (endpointMetadata.Endpoint.capabilities as EndpointCapabilities[]) ?? [], hostName: endpointMetadata.host.name, - platform: endpointMetadata.host.os.name.toLowerCase() as Platform, + platform: getHostPlatform(endpointMetadata), }); }, children: ( diff --git a/x-pack/plugins/security_solution/scripts/openapi/generate.js b/x-pack/plugins/security_solution/scripts/openapi/generate.js index 38eb0fe06f95a..adfe11192ae49 100644 --- a/x-pack/plugins/security_solution/scripts/openapi/generate.js +++ b/x-pack/plugins/security_solution/scripts/openapi/generate.js @@ -18,7 +18,6 @@ const SECURITY_SOLUTION_ROOT = resolve(__dirname, '../..'); rootDir: SECURITY_SOLUTION_ROOT, sourceGlob: './common/**/*.schema.yaml', templateName: 'zod_operation_schema', - skipLinting: true, }); await generate({ diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.test.ts index 6490c1f0f18ec..2cc6d8efd199e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.test.ts @@ -80,7 +80,12 @@ describe('Response Actions file download API', () => { it('should error if user has no authz to api', async () => { ( (await httpHandlerContextMock.securitySolution).getEndpointAuthz as jest.Mock - ).mockResolvedValue(getEndpointAuthzInitialStateMock({ canWriteFileOperations: false })); + ).mockResolvedValue( + getEndpointAuthzInitialStateMock({ + canWriteFileOperations: false, + canWriteExecuteOperations: false, + }) + ); await apiTestSetup .getRegisteredVersionedRoute('get', ACTION_AGENT_FILE_DOWNLOAD_ROUTE, '2023-10-31') 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 0037d5dded81f..7095b7d87a50c 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 @@ -47,7 +47,7 @@ export const registerActionFileDownloadRoutes = ( }, }, withEndpointAuthz( - { all: ['canWriteFileOperations'] }, + { any: ['canWriteFileOperations', 'canWriteExecuteOperations'] }, logger, getActionFileDownloadRouteHandler(endpointContext) ) diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.test.ts index e6554ee14ad6d..e9914dc4232d9 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.test.ts @@ -69,7 +69,12 @@ describe('Response Action file info API', () => { it('should error if user has no authz to api', async () => { ( (await httpHandlerContextMock.securitySolution).getEndpointAuthz as jest.Mock - ).mockResolvedValue(getEndpointAuthzInitialStateMock({ canWriteFileOperations: false })); + ).mockResolvedValue( + getEndpointAuthzInitialStateMock({ + canWriteFileOperations: false, + canWriteExecuteOperations: false, + }) + ); await apiTestSetup .getRegisteredVersionedRoute('get', ACTION_AGENT_FILE_INFO_ROUTE, '2023-10-31') 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 abc576fe3c9d9..a84f3b3a8bf6f 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 @@ -83,7 +83,7 @@ export const registerActionFileInfoRoute = ( }, }, withEndpointAuthz( - { all: ['canWriteFileOperations'] }, + { any: ['canWriteFileOperations', 'canWriteExecuteOperations'] }, endpointContext.logFactory.get('actionFileInfo'), getActionFileInfoRouteHandler(endpointContext) ) diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts b/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts index 8822db6c68367..a241148c7b714 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts @@ -6,6 +6,7 @@ */ import type { RequestHandler, Logger } from '@kbn/core/server'; +import { stringify } from '../utils/stringify'; import type { EndpointAuthzKeyList } from '../../../common/endpoint/types/authz'; import type { SecuritySolutionRequestHandlerContext } from '../../types'; import { EndpointAuthorizationError } from '../errors'; @@ -39,6 +40,21 @@ export const withEndpointAuthz = ( const validateAll = needAll.length > 0; const validateAny = needAny.length > 0; const enforceAuthz = validateAll || validateAny; + const logAuthzFailure = ( + user: string, + authzValidationResults: Record, + needed: string[] + ) => { + logger.debug( + `Unauthorized: user ${user} ${ + needed === needAll ? 'needs ALL' : 'needs at least one' + } of the following privileges:\n${stringify(needed)}\nbut is missing: ${stringify( + Object.entries(authzValidationResults) + .filter(([_, value]) => !value) + .map(([key]) => key) + )}` + ); + }; if (!enforceAuthz) { logger.warn(`Authorization disabled for API route: ${new Error('').stack ?? '?'}`); @@ -51,18 +67,37 @@ export const withEndpointAuthz = ( SecuritySolutionRequestHandlerContext > = async (context, request, response) => { if (enforceAuthz) { + const coreServices = await context.core; const endpointAuthz = await (await context.securitySolution).getEndpointAuthz(); - const permissionChecker = (permission: EndpointAuthzKeyList[0]) => endpointAuthz[permission]; + let authzValidationResults: Record = {}; + const permissionChecker = (permission: EndpointAuthzKeyList[0]) => { + authzValidationResults[permission] = endpointAuthz[permission]; + return endpointAuthz[permission]; + }; // has `all`? if (validateAll && !needAll.every(permissionChecker)) { + logAuthzFailure( + coreServices.security.authc.getCurrentUser()?.username ?? '', + authzValidationResults, + needAll + ); + return response.forbidden({ body: new EndpointAuthorizationError({ need_all: [...needAll] }), }); } + authzValidationResults = {}; + // has `any`? if (validateAny && !needAny.some(permissionChecker)) { + logAuthzFailure( + coreServices.security.authc.getCurrentUser()?.username ?? '', + authzValidationResults, + needAny + ); + return response.forbidden({ body: new EndpointAuthorizationError({ need_any: [...needAny] }), }); diff --git a/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts b/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts index 7a38948c0c46d..46f85a01f4760 100644 --- a/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts +++ b/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts @@ -683,7 +683,8 @@ describe('telemetry tasks', () => { }); }); - describe('telemetry-prebuilt-rule-alerts', () => { + // FLAKY: https://github.com/elastic/kibana/issues/188234 + describe.skip('telemetry-prebuilt-rule-alerts', () => { it('should execute when scheduled', async () => { await mockAndSchedulePrebuiltRulesTask(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts index 819bf87165e12..687bf91655e2a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -47,6 +47,9 @@ export const getOutputRuleAlertForRest = (): RuleResponse => ({ from: 'now-6m', id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], interval: '5m', risk_score: 50, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts index 767c01f02b187..4adf71d258e0a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts @@ -76,6 +76,9 @@ const reported = { from: 'now-6m', id: 'rule-id', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], investigation_fields: undefined, language: 'kuery', 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 index 452f59df8dcf9..349f54b1e3b3c 100644 --- 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 @@ -17,6 +17,7 @@ import { } from '../../../normalization/rule_actions'; import { typeSpecificCamelToSnake } from './type_specific_camel_to_snake'; import { commonParamsCamelToSnake } from './common_params_camel_to_snake'; +import { normalizeRuleParams } from './normalize_rule_params'; export const internalRuleToAPIResponse = ( rule: SanitizedRule | ResolvedSanitizedRule @@ -31,6 +32,7 @@ export const internalRuleToAPIResponse = ( const alertActions = rule.actions.map(transformAlertToRuleAction); const throttle = transformFromAlertThrottle(rule); const actions = transformToActionFrequency(alertActions, throttle); + const normalizedRuleParams = normalizeRuleParams(rule.params); return { // saved object properties @@ -49,7 +51,7 @@ export const internalRuleToAPIResponse = ( enabled: rule.enabled, revision: rule.revision, // Security solution shared rule params - ...commonParamsCamelToSnake(rule.params), + ...commonParamsCamelToSnake(normalizedRuleParams), // Type specific security solution rule params ...typeSpecificCamelToSnake(rule.params), // Actions diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.test.ts new file mode 100644 index 0000000000000..b8b5db137583b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.test.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 { normalizeRuleSource } from './normalize_rule_params'; +import type { BaseRuleParams } from '../../../../rule_schema'; + +describe('normalizeRuleSource', () => { + it('should return rule_source of type `internal` when immutable is false and ruleSource is undefined', () => { + const result = normalizeRuleSource({ + immutable: false, + ruleSource: undefined, + }); + expect(result).toEqual({ + type: 'internal', + }); + }); + + it('should return rule_source of type `external` and `isCustomized: false` when immutable is true and ruleSource is undefined', () => { + const result = normalizeRuleSource({ + immutable: true, + ruleSource: undefined, + }); + expect(result).toEqual({ + type: 'external', + isCustomized: false, + }); + }); + + it('should return existing value when ruleSource is present', () => { + const externalRuleSource: BaseRuleParams['ruleSource'] = { + type: 'external', + isCustomized: true, + }; + const externalResult = normalizeRuleSource({ immutable: true, ruleSource: externalRuleSource }); + expect(externalResult).toEqual({ + type: externalRuleSource.type, + isCustomized: externalRuleSource.isCustomized, + }); + + const internalRuleSource: BaseRuleParams['ruleSource'] = { + type: 'internal', + }; + const internalResult = normalizeRuleSource({ + immutable: false, + ruleSource: internalRuleSource, + }); + expect(internalResult).toEqual({ + type: internalRuleSource.type, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.ts new file mode 100644 index 0000000000000..eddd8b0434ba0 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { BaseRuleParams, RuleSourceCamelCased } from '../../../../rule_schema'; + +interface NormalizeRuleSourceParams { + immutable: BaseRuleParams['immutable']; + ruleSource: BaseRuleParams['ruleSource']; +} + +/* + * Since there's no mechanism to migrate all rules at the same time, + * we cannot guarantee that the ruleSource params is present in all rules. + * This function will normalize the ruleSource param, creating it if does + * not exist in ES, based on the immutable param. + */ +export const normalizeRuleSource = ({ + immutable, + ruleSource, +}: NormalizeRuleSourceParams): RuleSourceCamelCased => { + if (!ruleSource) { + const normalizedRuleSource: RuleSourceCamelCased = immutable + ? { + type: 'external', + isCustomized: false, + } + : { + type: 'internal', + }; + + return normalizedRuleSource; + } + return ruleSource; +}; + +export const normalizeRuleParams = (params: BaseRuleParams) => { + return { + ...params, + // Fields to normalize + ruleSource: normalizeRuleSource({ + immutable: params.immutable, + ruleSource: params.ruleSource, + }), + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 0ba0afbce715a..382df4bfa5ffc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -100,6 +100,9 @@ describe('getExportAll', () => { from: 'now-6m', id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], interval: '5m', rule_id: 'rule-1', @@ -280,6 +283,9 @@ describe('getExportAll', () => { from: 'now-6m', id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], interval: '5m', rule_id: 'rule-1', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index f11e31691d25b..c9a5a93a4f1c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -8,85 +8,15 @@ import { transformValidateBulkError } from './validate'; import type { BulkError } from '../../routes/utils'; import { getRuleMock } from '../../routes/__mocks__/request_responses'; -import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; -import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; import { getQueryRuleParams } from '../../rule_schema/mocks'; -import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; - -export const ruleOutput = (): RuleResponse => ({ - actions: [], - author: ['Elastic'], - building_block_type: 'default', - created_at: '2019-12-13T16:40:33.400Z', - updated_at: '2019-12-13T16:40:33.400Z', - created_by: 'elastic', - description: 'Detecting root and admin users', - enabled: true, - false_positives: [], - from: 'now-6m', - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - immutable: false, - interval: '5m', - rule_id: 'rule-1', - language: 'kuery', - license: 'Elastic License', - output_index: '.siem-signals', - max_signals: 10000, - risk_score: 50, - risk_score_mapping: [], - name: 'Detect Root/Admin Users', - query: 'user.name: root or user.name: admin', - references: ['http://example.com', 'https://example.com'], - severity: 'high', - severity_mapping: [], - updated_by: 'elastic', - tags: [], - to: 'now', - type: 'query', - throttle: undefined, - threat: getThreatMock(), - version: 1, - revision: 0, - filters: [ - { - query: { - match_phrase: { - 'host.name': 'some-host', - }, - }, - }, - ], - exceptions_list: getListArrayMock(), - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - meta: { - someMeta: 'someField', - }, - note: '# Investigative notes', - timeline_title: 'some-timeline-title', - timeline_id: 'some-timeline-id', - related_integrations: [], - required_fields: [], - response_actions: undefined, - setup: '', - outcome: undefined, - alias_target_id: undefined, - alias_purpose: undefined, - rule_name_override: undefined, - timestamp_override: undefined, - timestamp_override_fallback_disabled: undefined, - namespace: undefined, - data_view_id: undefined, - saved_id: undefined, - alert_suppression: undefined, - investigation_fields: undefined, -}); +import { getOutputRuleAlertForRest } from '../../routes/__mocks__/utils'; describe('validate', () => { describe('transformValidateBulkError', () => { test('it should do a validation correctly of a rule id', () => { const ruleAlert = getRuleMock(getQueryRuleParams()); const validatedOrError = transformValidateBulkError('rule-1', ruleAlert); - expect(validatedOrError).toEqual(ruleOutput()); + expect(validatedOrError).toEqual(getOutputRuleAlertForRest()); }); test('it should do an in-validation correctly of a rule id', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index 3a4fa1dadd778..8099d7a00049f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -32,6 +32,9 @@ export const getBaseRuleParams = (): BaseRuleParams => { description: 'Detecting root and admin users', falsePositives: [], immutable: false, + ruleSource: { + type: 'internal', + }, from: 'now-6m', to: 'now', severity: 'high', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts index ffb5f6ee45170..4aaa0189eefc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts @@ -162,6 +162,9 @@ describe('buildAlert', () => { }, ], immutable: false, + rule_source: { + type: 'internal', + }, type: 'query', language: 'kuery', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], @@ -357,6 +360,9 @@ describe('buildAlert', () => { }, ], immutable: false, + rule_source: { + type: 'internal', + }, type: 'query', language: 'kuery', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts index ac22303c09af6..4770d051f2e99 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts @@ -11,7 +11,7 @@ import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import type { AuditLogger } from '@kbn/security-plugin-types-server'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import type { - AssetCriticalityBulkUploadResponse, + BulkUpsertAssetCriticalityRecordsResponse, AssetCriticalityUpsert, } from '../../../../common/entity_analytics/asset_criticality/types'; import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics'; @@ -211,9 +211,9 @@ export class AssetCriticalityDataClient { recordsStream, flushBytes, retries, - }: BulkUpsertFromStreamOptions): Promise => { - const errors: AssetCriticalityBulkUploadResponse['errors'] = []; - const stats: AssetCriticalityBulkUploadResponse['stats'] = { + }: BulkUpsertFromStreamOptions): Promise => { + const errors: BulkUpsertAssetCriticalityRecordsResponse['errors'] = []; + const stats: BulkUpsertAssetCriticalityRecordsResponse['stats'] = { successful: 0, failed: 0, total: 0, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/bulk_upload.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/bulk_upload.ts index e1eb6872d3a33..822c8a644d9b3 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/bulk_upload.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/bulk_upload.ts @@ -9,8 +9,8 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; import { Readable } from 'node:stream'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; -import type { AssetCriticalityBulkUploadResponse } from '../../../../../common/api/entity_analytics'; -import { AssetCriticalityBulkUploadRequest } from '../../../../../common/api/entity_analytics'; +import type { BulkUpsertAssetCriticalityRecordsResponse } from '../../../../../common/api/entity_analytics'; +import { BulkUpsertAssetCriticalityRecordsRequestBody } from '../../../../../common/api/entity_analytics'; import type { ConfigType } from '../../../../config'; import { ASSET_CRITICALITY_PUBLIC_BULK_UPLOAD_URL, @@ -42,7 +42,7 @@ export const assetCriticalityPublicBulkUploadRoute = ( version: API_VERSIONS.public.v1, validate: { request: { - body: buildRouteValidationWithZod(AssetCriticalityBulkUploadRequest), + body: buildRouteValidationWithZod(BulkUpsertAssetCriticalityRecordsRequestBody), }, }, }, @@ -90,7 +90,7 @@ export const assetCriticalityPublicBulkUploadRoute = ( () => `Asset criticality Bulk upload completed in ${tookMs}ms ${JSON.stringify(stats)}` ); - const resBody: AssetCriticalityBulkUploadResponse = { errors, stats }; + const resBody: BulkUpsertAssetCriticalityRecordsResponse = { errors, stats }; return response.ok({ body: resBody }); } catch (e) { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts index c7a0f07400cc8..b39013359eed4 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts @@ -8,6 +8,10 @@ import type { IKibanaResponse, KibanaResponseFactory, Logger } from '@kbn/core/s import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; +import { + DeleteAssetCriticalityRecordRequestQuery, + InternalDeleteAssetCriticalityRecordRequestQuery, +} from '../../../../../common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen'; import type { SecuritySolutionRequestHandlerContext } from '../../../../types'; import { ASSET_CRITICALITY_PUBLIC_URL, @@ -16,7 +20,6 @@ import { ENABLE_ASSET_CRITICALITY_SETTING, API_VERSIONS, } from '../../../../../common/constants'; -import { DeleteAssetCriticalityRecord } from '../../../../../common/api/entity_analytics/asset_criticality'; import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources'; import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled'; import type { EntityAnalyticsRoutesDeps } from '../../types'; @@ -26,7 +29,7 @@ import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; type DeleteHandler = ( context: SecuritySolutionRequestHandlerContext, request: { - query: DeleteAssetCriticalityRecord; + query: DeleteAssetCriticalityRecordRequestQuery; }, response: KibanaResponseFactory ) => Promise; @@ -88,7 +91,7 @@ export const assetCriticalityInternalDeleteRoute = ( version: API_VERSIONS.internal.v1, validate: { request: { - query: buildRouteValidationWithZod(DeleteAssetCriticalityRecord), + query: buildRouteValidationWithZod(InternalDeleteAssetCriticalityRecordRequestQuery), }, }, }, @@ -113,7 +116,7 @@ export const assetCriticalityPublicDeleteRoute = ( version: API_VERSIONS.public.v1, validate: { request: { - query: buildRouteValidationWithZod(DeleteAssetCriticalityRecord), + query: buildRouteValidationWithZod(DeleteAssetCriticalityRecordRequestQuery), }, }, }, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts index 07d0cb3098dbc..e1ab013a373b6 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts @@ -8,6 +8,7 @@ import type { IKibanaResponse, KibanaResponseFactory, Logger } from '@kbn/core/s import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; +import { GetAssetCriticalityRecordRequestQuery } from '../../../../../common/api/entity_analytics/asset_criticality/get_asset_criticality.gen'; import type { SecuritySolutionRequestHandlerContext } from '../../../../types'; import { ASSET_CRITICALITY_INTERNAL_URL, @@ -17,7 +18,6 @@ import { API_VERSIONS, } from '../../../../../common/constants'; import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources'; -import { AssetCriticalityRecordIdParts } from '../../../../../common/api/entity_analytics/asset_criticality'; import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled'; import type { EntityAnalyticsRoutesDeps } from '../../types'; import { AssetCriticalityAuditActions } from '../audit'; @@ -25,7 +25,7 @@ import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; type GetHandler = ( context: SecuritySolutionRequestHandlerContext, request: { - query: AssetCriticalityRecordIdParts; + query: GetAssetCriticalityRecordRequestQuery; }, response: KibanaResponseFactory ) => Promise; @@ -86,7 +86,7 @@ export const assetCriticalityInternalGetRoute = ( version: API_VERSIONS.internal.v1, validate: { request: { - query: buildRouteValidationWithZod(AssetCriticalityRecordIdParts), + query: buildRouteValidationWithZod(GetAssetCriticalityRecordRequestQuery), }, }, }, @@ -111,7 +111,7 @@ export const assetCriticalityPublicGetRoute = ( version: API_VERSIONS.public.v1, validate: { request: { - query: buildRouteValidationWithZod(AssetCriticalityRecordIdParts), + query: buildRouteValidationWithZod(GetAssetCriticalityRecordRequestQuery), }, }, }, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/list.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/list.ts index 66db32f2bdb17..711426e4df510 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/list.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/list.ts @@ -15,8 +15,8 @@ import { API_VERSIONS, } from '../../../../../common/constants'; import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources'; -import type { AssetCriticalityListResponse } from '../../../../../common/api/entity_analytics/asset_criticality'; -import { ListAssetCriticalityQueryParams } from '../../../../../common/api/entity_analytics/asset_criticality'; +import type { FindAssetCriticalityRecordsResponse } from '../../../../../common/api/entity_analytics/asset_criticality'; +import { FindAssetCriticalityRecordsRequestQuery } from '../../../../../common/api/entity_analytics/asset_criticality'; import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled'; import type { EntityAnalyticsRoutesDeps } from '../../types'; import { AssetCriticalityAuditActions } from '../audit'; @@ -39,7 +39,7 @@ export const assetCriticalityPublicListRoute = ( version: API_VERSIONS.public.v1, validate: { request: { - query: buildRouteValidationWithZod(ListAssetCriticalityQueryParams), + query: buildRouteValidationWithZod(FindAssetCriticalityRecordsRequestQuery), }, }, }, @@ -81,7 +81,7 @@ export const assetCriticalityPublicListRoute = ( }, }); - const body: AssetCriticalityListResponse = { + const body: FindAssetCriticalityRecordsResponse = { records, total, page, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts index 2afa73ed5a059..9d77817a20d98 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts @@ -7,7 +7,7 @@ import type { Logger } from '@kbn/core/server'; import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; -import type { AssetCriticalityStatusResponse } from '../../../../../common/api/entity_analytics/asset_criticality'; +import type { GetAssetCriticalityStatusResponse } from '../../../../../common/api/entity_analytics'; import { ASSET_CRITICALITY_INTERNAL_STATUS_URL, APP_ID, @@ -55,7 +55,7 @@ export const assetCriticalityInternalStatusRoute = ( }, }); - const body: AssetCriticalityStatusResponse = { + const body: GetAssetCriticalityStatusResponse = { asset_criticality_resources_installed: result.isAssetCriticalityResourcesInstalled, }; return response.ok({ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts index 28c8333c5f596..7e284bfe042a0 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts @@ -10,7 +10,7 @@ import { schema } from '@kbn/config-schema'; import Papa from 'papaparse'; import { transformError } from '@kbn/securitysolution-es-utils'; import type internal from 'stream'; -import type { AssetCriticalityBulkUploadResponse } from '../../../../../common/api/entity_analytics'; +import type { UploadAssetCriticalityRecordsResponse } from '../../../../../common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen'; import { CRITICALITY_CSV_MAX_SIZE_BYTES_WITH_TOLERANCE } from '../../../../../common/entity_analytics/asset_criticality'; import type { ConfigType } from '../../../../config'; import type { HapiReadableStream, SecuritySolutionRequestHandlerContext } from '../../../../types'; @@ -90,7 +90,7 @@ const handler: ( ); // type assignment here to ensure that the response body stays in sync with the API schema - const resBody: AssetCriticalityBulkUploadResponse = { errors, stats }; + const resBody: UploadAssetCriticalityRecordsResponse = { errors, stats }; const [eventType, event] = createAssetCriticalityProcessedFileEvent({ startTime: start, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts index cb3c36f450e43..20ad8173af666 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts @@ -8,6 +8,10 @@ import type { IKibanaResponse, KibanaResponseFactory, Logger } from '@kbn/core/s import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; +import { + CreateAssetCriticalityRecordRequestBody, + InternalCreateAssetCriticalityRecordRequestBody, +} from '../../../../../common/api/entity_analytics/asset_criticality/create_asset_criticality.gen'; import type { SecuritySolutionRequestHandlerContext } from '../../../../types'; import { ASSET_CRITICALITY_PUBLIC_URL, @@ -17,7 +21,6 @@ import { API_VERSIONS, } from '../../../../../common/constants'; import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources'; -import { CreateSingleAssetCriticalityRequest } from '../../../../../common/api/entity_analytics'; import type { EntityAnalyticsRoutesDeps } from '../../types'; import { AssetCriticalityAuditActions } from '../audit'; import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; @@ -26,7 +29,7 @@ import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setti type UpsertHandler = ( context: SecuritySolutionRequestHandlerContext, request: { - body: CreateSingleAssetCriticalityRequest; + body: CreateAssetCriticalityRecordRequestBody; }, response: KibanaResponseFactory ) => Promise; @@ -93,7 +96,7 @@ export const assetCriticalityInternalUpsertRoute = ( version: API_VERSIONS.internal.v1, validate: { request: { - body: buildRouteValidationWithZod(CreateSingleAssetCriticalityRequest), + body: buildRouteValidationWithZod(InternalCreateAssetCriticalityRecordRequestBody), }, }, }, @@ -118,7 +121,7 @@ export const assetCriticalityPublicUpsertRoute = ( version: API_VERSIONS.public.v1, validate: { request: { - body: buildRouteValidationWithZod(CreateSingleAssetCriticalityRequest), + body: buildRouteValidationWithZod(CreateAssetCriticalityRecordRequestBody), }, }, }, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts index f1f0348a69e33..3501d1869d5ed 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts @@ -7,7 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; -import type { RiskEngineDisableResponse } from '../../../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; +import type { DisableRiskEngineResponse } from '../../../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; import { RISK_ENGINE_DISABLE_URL, APP_ID } from '../../../../../common/constants'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; @@ -71,7 +71,7 @@ export const riskEngineDisableRoute = ( try { await riskEngineClient.disableRiskEngine({ taskManager }); - const body: RiskEngineDisableResponse = { success: true }; + const body: DisableRiskEngineResponse = { success: true }; return response.ok({ body }); } catch (e) { const error = transformError(e); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts index a4eed8701d1e1..9397af65675da 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts @@ -7,7 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; -import type { RiskEngineEnableResponse } from '../../../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; +import type { EnableRiskEngineResponse } from '../../../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; import { RISK_ENGINE_ENABLE_URL, APP_ID } from '../../../../../common/constants'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; @@ -69,7 +69,7 @@ export const riskEngineEnableRoute = ( try { await riskEngineClient.enableRiskEngine({ taskManager }); - const body: RiskEngineEnableResponse = { success: true }; + const body: EnableRiskEngineResponse = { success: true }; return response.ok({ body }); } catch (e) { const error = transformError(e); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts index 8360f3652a7f3..9e50e0b98ccd8 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts @@ -8,8 +8,8 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; import type { - RiskEngineInitResponse, - RiskEngineInitResult, + InitRiskEngineResponse, + InitRiskEngineResult, } from '../../../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; import { RISK_ENGINE_INIT_URL, APP_ID } from '../../../../../common/constants'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; @@ -64,7 +64,7 @@ export const riskEngineInitRoute = ( riskScoreDataClient, }); - const result: RiskEngineInitResult = { + const result: InitRiskEngineResult = { risk_engine_enabled: initResult.riskEngineEnabled, risk_engine_resources_installed: initResult.riskEngineResourcesInstalled, risk_engine_configuration_created: initResult.riskEngineConfigurationCreated, @@ -72,7 +72,7 @@ export const riskEngineInitRoute = ( errors: initResult.errors, }; - const initResponse: RiskEngineInitResponse = { + const initResponse: InitRiskEngineResponse = { result, }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts index 032114f7871b6..1d39fbaf18420 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts @@ -7,7 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; -import type { RiskEngineSettingsResponse } from '../../../../../common/api/entity_analytics/risk_engine'; +import type { ReadRiskEngineSettingsResponse } from '../../../../../common/api/entity_analytics/risk_engine'; import { RISK_ENGINE_SETTINGS_URL, APP_ID } from '../../../../../common/constants'; import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; import type { EntityAnalyticsRoutesDeps } from '../../types'; @@ -43,7 +43,7 @@ export const riskEngineSettingsRoute = (router: EntityAnalyticsRoutesDeps['route if (!result) { throw new Error('Unable to get risk engine configuration'); } - const body: RiskEngineSettingsResponse = { + const body: ReadRiskEngineSettingsResponse = { range: result.range, }; return response.ok({ diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts b/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts index 97a4d44fcd594..8eb46b2046c10 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts @@ -5,7 +5,7 @@ * 2.0. */ import type { EventTypeOpts } from '@kbn/core/server'; -import type { AssetCriticalityBulkUploadResponse } from '../../../../common/api/entity_analytics'; +import type { BulkUpsertAssetCriticalityRecordsResponse } from '../../../../common/api/entity_analytics'; export const RISK_SCORE_EXECUTION_SUCCESS_EVENT: EventTypeOpts<{ scoresWritten: number; @@ -88,7 +88,7 @@ interface AssetCriticalitySystemProcessedAssignmentFileEvent { endTime: string; tookMs: number; }; - result?: AssetCriticalityBulkUploadResponse['stats']; + result?: BulkUpsertAssetCriticalityRecordsResponse['stats']; status: 'success' | 'partial_success' | 'fail'; } @@ -124,7 +124,7 @@ export const ASSET_CRITICALITY_SYSTEM_PROCESSED_ASSIGNMENT_FILE_EVENT: EventType }; interface CreateAssetCriticalityProcessedFileEvent { - result?: AssetCriticalityBulkUploadResponse['stats']; + result?: BulkUpsertAssetCriticalityRecordsResponse['stats']; startTime: Date; endTime: Date; } @@ -154,7 +154,7 @@ export const createAssetCriticalityProcessedFileEvent = ({ ]; }; -const getUploadStatus = (stats?: AssetCriticalityBulkUploadResponse['stats']) => { +const getUploadStatus = (stats?: BulkUpsertAssetCriticalityRecordsResponse['stats']) => { if (!stats) { return 'fail'; } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/gemini/constants.tsx b/x-pack/plugins/stack_connectors/public/connector_types/gemini/constants.tsx index 162f78efabc48..e9844a1c39b03 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/gemini/constants.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/gemini/constants.tsx @@ -27,7 +27,7 @@ const contents = [ role: 'user', parts: [ { - text: 'Write the first line of a story about a magic backpack.', + text: 'Hello world!', }, ], }, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 43b0fdd793da6..4b263c9f5a080 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -14151,13 +14151,6 @@ "xpack.enterpriseSearch.aiSearch.elser.description.elserLinkText": "Elastic Learned Sparse Encoder v2", "xpack.enterpriseSearch.aiSearch.elserAccordion.description": "Fonctionnalités de recherche sémantique instantanée", "xpack.enterpriseSearch.aiSearch.elserAccordion.title": "Elastic Learned Sparse Encoder", - "xpack.enterpriseSearch.aiSearch.elserPanel.step1.buttonLabel": "Créer un index", - "xpack.enterpriseSearch.aiSearch.elserPanel.step1.title": "Créez un index", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.description": "Après avoir créé un index, sélectionnez-le et cliquez sur l'onglet intitulé {pipelinesName}.", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.description.pipelinesName": "Pipelines", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.title": "Accédez à l’onglet Pipelines d’un index", - "xpack.enterpriseSearch.aiSearch.elserPanel.step3.description": "Localisez le panneau qui vous permet de déployer ELSER en un clic et créez un pipeline d’inférence à l’aide de ce modèle.", - "xpack.enterpriseSearch.aiSearch.elserPanel.step3.title": "Suivez les instructions à l’écran pour déployer ELSER", "xpack.enterpriseSearch.aiSearch.guide.description": "Élaborez une application de recherche propulsée par l'intelligence artificielle grâce à la plateforme Elastic, y compris notre modèle ML entraîné ELSER, notre recherche vectorielle et nos capacités d'intégration, ainsi que le classement RRF pour combiner la recherche vectorielle et la recherche textuelle.", "xpack.enterpriseSearch.aiSearch.guide.pageTitle": "Améliorez vos recherches avec l'intelligence artificielle", "xpack.enterpriseSearch.aiSearch.linearCombinationAccordion.description": "Résultats pondérés de plusieurs classements", @@ -14208,14 +14201,6 @@ "xpack.enterpriseSearch.aiSearch.vectorSearchAccordion.title": "Recherche vectorielle", "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description": "Utilisez des {vectorDbCapabilities} en ajoutant des incorporations de vos modèles ML. Déployez des modèles entraînés sur des nœuds de ML Elastic et configurez des pipelines d’inférence pour ajouter automatiquement des incorporations quand vous ingérez des documents, afin de pouvoir utiliser la méthode de recherche vectorielle kNN dans _search.", "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description.vectorDbCapabilitiesLinkText": "Fonctionnalités de bases de données vectorielles d’Elasticsearch", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.buttonLabel": "Affichez les modèles entraînés", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.guideToTrainedModelsLinkText": "Guide sur les modèles entraînés", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.title": "Apprenez à charger des modèles de ML", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.buttonLabel": "Créez un index", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.title": "Créez un index", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.description": "Accédez à l'onglet {pipelinesName} de votre index pour créer un pipeline d'inférence qui utilise votre modèle déployé.", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.description.pipelinesName": "Pipelines", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.title": "Créez un pipeline d’inférence de ML", "xpack.enterpriseSearch.analytics..units.quickRange.last1Year": "Dernière année", "xpack.enterpriseSearch.analytics..units.quickRange.last2Weeks": "Deux dernières semaines", "xpack.enterpriseSearch.analytics..units.quickRange.last30Days": "30 derniers jours", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c3ae051fef551..2a44c2da7e209 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -14107,13 +14107,6 @@ "xpack.enterpriseSearch.aiSearch.elser.description.elserLinkText": "Elastic Learned Sparse Encoder v2", "xpack.enterpriseSearch.aiSearch.elserAccordion.description": "即時セマンティック検索機能", "xpack.enterpriseSearch.aiSearch.elserAccordion.title": "Elastic Learned Sparse Encoder", - "xpack.enterpriseSearch.aiSearch.elserPanel.step1.buttonLabel": "インデックスを作成", - "xpack.enterpriseSearch.aiSearch.elserPanel.step1.title": "インデックスを作成", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.description": "インデックスを作成した後は、インデックスを選択し、{pipelinesName}タブをクリックします。", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.description.pipelinesName": "パイプライン", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.title": "インデックスのパイプラインタブに移動", - "xpack.enterpriseSearch.aiSearch.elserPanel.step3.description": "ELSERをワンクリックでデプロイし、そのモデルを使った推論パイプラインを作成できるパネルを探します。", - "xpack.enterpriseSearch.aiSearch.elserPanel.step3.title": "画面の指示に従い、ELSERをデプロイ", "xpack.enterpriseSearch.aiSearch.guide.description": "当社独自の学習済みMLモデルELSER、ベクトル検索と埋め込み機能、ベクトル検索とテキスト検索を組み合わせたRRFランキングなど、Elasticプラットフォームを使用して、AI検索を活用したアプリケーションを構築できます。", "xpack.enterpriseSearch.aiSearch.guide.pageTitle": "AIで検索を強化", "xpack.enterpriseSearch.aiSearch.linearCombinationAccordion.description": "複数のランキングから重み付けがされた結果", @@ -14164,14 +14157,6 @@ "xpack.enterpriseSearch.aiSearch.vectorSearchAccordion.title": "ベクトル検索", "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description": "MLモデルから埋め込みを追加して、{vectorDbCapabilities}を使用します。Elastic MLノードに学習済みモデルをデプロイし、推論パイプラインを設定して、ドキュメントをインジェストしたときに自動的に埋め込みが追加されるようにします。これにより、_searchでkNNベクトル検索方法を使用できます。", "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description.vectorDbCapabilitiesLinkText": "ElasticsearchのベクトルDB機能", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.buttonLabel": "学習済みモデルを表示", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.guideToTrainedModelsLinkText": "学習済みモデルのガイド", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.title": "MLモデルのアップロード方法", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.buttonLabel": "インデックスを作成", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.title": "インデックスを作成", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.description": "インデックスの{pipelinesName}タブに移動し、デプロイされたモデルで使用する推論パイプラインを作成します。", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.description.pipelinesName": "パイプライン", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.title": "ML推論パイプラインを作成", "xpack.enterpriseSearch.analytics..units.quickRange.last1Year": "過去1年間", "xpack.enterpriseSearch.analytics..units.quickRange.last2Weeks": "過去 2 週間", "xpack.enterpriseSearch.analytics..units.quickRange.last30Days": "過去30日間", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f96a1de934d35..25a0b95a991fd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -14171,13 +14171,6 @@ "xpack.enterpriseSearch.aiSearch.elser.description.elserLinkText": "Elastic Learned Sparse Encoder v2", "xpack.enterpriseSearch.aiSearch.elserAccordion.description": "即时语义搜索功能", "xpack.enterpriseSearch.aiSearch.elserAccordion.title": "Elastic Learned Sparse Encoder", - "xpack.enterpriseSearch.aiSearch.elserPanel.step1.buttonLabel": "创建索引", - "xpack.enterpriseSearch.aiSearch.elserPanel.step1.title": "创建索引", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.description": "创建索引后,请选中该索引,然后单击 {pipelinesName} 选项卡。", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.description.pipelinesName": "管道", - "xpack.enterpriseSearch.aiSearch.elserPanel.step2.title": "导航到索引的“管道”选项卡", - "xpack.enterpriseSearch.aiSearch.elserPanel.step3.description": "查找允许您一键部署 ELSER 并使用该模型创建推理管道的面板。", - "xpack.enterpriseSearch.aiSearch.elserPanel.step3.title": "按照屏幕上显示的说明部署 ELSER", "xpack.enterpriseSearch.aiSearch.guide.description": "使用 Elastic 平台,包括我们专有的已训练 ML 模型 ELSER、矢量搜索和嵌入功能,以及用于组合矢量和文本搜索的 RRF 排名,构建 AI 搜索驱动式应用程序。", "xpack.enterpriseSearch.aiSearch.guide.pageTitle": "利用 AI 增强您的搜索功能", "xpack.enterpriseSearch.aiSearch.linearCombinationAccordion.description": "来自多个排名的加权结果", @@ -14228,14 +14221,6 @@ "xpack.enterpriseSearch.aiSearch.vectorSearchAccordion.title": "矢量搜索", "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description": "通过添加来自 ML 模型的嵌入来使用{vectorDbCapabilities}。在 Elastic ML 节点上部署已训练模型并设置推理管道,以在采集文档时自动添加嵌入,便于您在 _search 中使用 kNN 矢量搜索方法。", "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.description.vectorDbCapabilitiesLinkText": "Elasticsearch 的矢量 DB 功能", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.buttonLabel": "查看已训练模型", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.guideToTrainedModelsLinkText": "已训练模型指南", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step1.title": "了解如何上传 ML 模型", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.buttonLabel": "创建索引", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step2.title": "创建索引", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.description": "导航到您索引的 {pipelinesName} 选项卡,以创建使用已部署模型的推理管道。", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.description.pipelinesName": "管道", - "xpack.enterpriseSearch.aiSearch.vectorSearchPanel.step3.title": "创建 ML 推理管道", "xpack.enterpriseSearch.analytics..units.quickRange.last1Year": "过去 1 年", "xpack.enterpriseSearch.analytics..units.quickRange.last2Weeks": "过去 2 周", "xpack.enterpriseSearch.analytics..units.quickRange.last30Days": "过去 30 天", diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts index d483d11db96ec..54eebf207e7d7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts @@ -310,7 +310,7 @@ export default function geminiTest({ getService }: FtrProviderContext) { role: 'user', parts: [ { - text: 'Write the first line of a story about a magic backpack.', + text: 'Hello world!', }, ], }, @@ -325,7 +325,7 @@ export default function geminiTest({ getService }: FtrProviderContext) { contents: [ { role: 'user', - parts: [{ text: 'Write the first line of a story about a magic backpack.' }], + parts: [{ text: 'Hello world!' }], }, ], generation_config: { temperature: 0, maxOutputTokens: 8192 }, diff --git a/x-pack/test/api_integration/services/security_solution_api.gen.ts b/x-pack/test/api_integration/services/security_solution_api.gen.ts index f5089b489a617..91ae460bbb563 100644 --- a/x-pack/test/api_integration/services/security_solution_api.gen.ts +++ b/x-pack/test/api_integration/services/security_solution_api.gen.ts @@ -26,13 +26,17 @@ import { BulkDeleteRulesRequestBodyInput } from '@kbn/security-solution-plugin/c import { BulkDeleteRulesPostRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_crud/bulk_delete_rules/bulk_delete_rules_route.gen'; import { BulkPatchRulesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_crud/bulk_patch_rules/bulk_patch_rules_route.gen'; import { BulkUpdateRulesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.gen'; +import { BulkUpsertAssetCriticalityRecordsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/bulk_upload_asset_criticality.gen'; import { CreateAlertsMigrationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen'; +import { CreateAssetCriticalityRecordRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/create_asset_criticality.gen'; import { CreateRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/create_rule/create_rule_route.gen'; import { CreateUpdateProtectionUpdatesNoteRequestParamsInput, CreateUpdateProtectionUpdatesNoteRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/endpoint/protection_updates_note/protection_updates_note.gen'; +import { DeleteAssetCriticalityRecordRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen'; import { DeleteRuleRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.gen'; +import { DeprecatedTriggerRiskScoreCalculationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; import { EndpointIsolateRedirectRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/isolate_route.gen'; import { EndpointUnisolateRedirectRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/unisolate_route.gen'; import { @@ -40,9 +44,11 @@ import { ExportRulesRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/export_rules/export_rules_route.gen'; import { FinalizeAlertsMigrationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen'; +import { FindAssetCriticalityRecordsRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/list_asset_criticality.gen'; import { FindRulesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/find_rules/find_rules_route.gen'; import { GetAgentPolicySummaryRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/policy.gen'; import { GetAlertsMigrationStatusRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.gen'; +import { GetAssetCriticalityRecordRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/get_asset_criticality.gen'; import { GetEndpointSuggestionsRequestParamsInput, GetEndpointSuggestionsRequestBodyInput, @@ -58,18 +64,22 @@ import { GetRuleExecutionResultsRequestParamsInput, } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen'; import { ImportRulesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/import_rules/import_rules_route.gen'; +import { InternalCreateAssetCriticalityRecordRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/create_asset_criticality.gen'; +import { InternalDeleteAssetCriticalityRecordRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen'; import { ManageAlertTagsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen'; import { PatchRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.gen'; import { PerformBulkActionRequestQueryInput, PerformBulkActionRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen'; +import { PreviewRiskScoreRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/risk_engine/preview_route.gen'; import { ReadRuleRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/read_rule/read_rule_route.gen'; import { RulePreviewRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_preview/rule_preview.gen'; import { SearchAlertsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals/query_signals/query_signals_route.gen'; import { SetAlertAssigneesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen'; import { SetAlertsStatusRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen'; import { SuggestUserProfilesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/users/suggest_user_profiles_route.gen'; +import { TriggerRiskScoreCalculationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; import { UpdateRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.gen'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -153,6 +163,14 @@ after 30 days. It also deletes other artifacts specific to the migration impleme .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + bulkUpsertAssetCriticalityRecords(props: BulkUpsertAssetCriticalityRecordsProps) { + return supertest + .post('/api/asset_criticality/bulk') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, createAlertsIndex() { return supertest .post('/api/detection_engine/index') @@ -173,6 +191,14 @@ Migrations are initiated per index. While the process is neither destructive nor .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + createAssetCriticalityRecord(props: CreateAssetCriticalityRecordProps) { + return supertest + .post('/api/asset_criticality') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Create a new detection rule. */ @@ -201,6 +227,14 @@ Migrations are initiated per index. While the process is neither destructive nor .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + deleteAssetCriticalityRecord(props: DeleteAssetCriticalityRecordProps) { + return supertest + .delete('/api/asset_criticality') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, /** * Delete a detection rule using the `rule_id` or `id` field. */ @@ -212,6 +246,31 @@ Migrations are initiated per index. While the process is neither destructive nor .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Calculates and persists Risk Scores for an entity, returning the calculated risk score. + */ + deprecatedTriggerRiskScoreCalculation(props: DeprecatedTriggerRiskScoreCalculationProps) { + return supertest + .post('/api/risk_scores/calculation/entity') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + disableRiskEngine() { + return supertest + .post('/internal/risk_score/engine/disable') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, + enableRiskEngine() { + return supertest + .post('/internal/risk_score/engine/enable') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, endpointIsolateRedirect(props: EndpointIsolateRedirectProps) { return supertest .post('/api/endpoint/isolate') @@ -259,6 +318,14 @@ finalize it. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + findAssetCriticalityRecords(props: FindAssetCriticalityRecordsProps) { + return supertest + .post('/api/asset_criticality/list') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, /** * Retrieve a paginated list of detection rules. By default, the first page is returned, with 20 results per page. */ @@ -296,6 +363,21 @@ finalize it. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + getAssetCriticalityRecord(props: GetAssetCriticalityRecordProps) { + return supertest + .get('/api/asset_criticality') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, + getAssetCriticalityStatus() { + return supertest + .get('/internal/asset_criticality/status') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, getEndpointSuggestions(props: GetEndpointSuggestionsProps) { return supertest .post(replaceParams('/api/endpoint/suggestions/{suggestion_type}', props.params)) @@ -345,6 +427,16 @@ detection engine rules. .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + /** + * Returns the status of both the legacy transform-based risk engine, as well as the new risk engine + */ + getRiskEngineStatus() { + return supertest + .get('/internal/risk_score/engine/status') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, getRuleExecutionEvents(props: GetRuleExecutionEventsProps) { return supertest .put( @@ -379,6 +471,16 @@ detection engine rules. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine + */ + initRiskEngine() { + return supertest + .post('/internal/risk_score/engine/init') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * Install and update all Elastic prebuilt detection rules and Timelines. */ @@ -389,6 +491,29 @@ detection engine rules. .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + internalCreateAssetCriticalityRecord(props: InternalCreateAssetCriticalityRecordProps) { + return supertest + .post('/internal/asset_criticality') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + internalDeleteAssetCriticalityRecord(props: InternalDeleteAssetCriticalityRecordProps) { + return supertest + .delete('/internal/asset_criticality') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, + internalUploadAssetCriticalityRecords() { + return supertest + .post('/internal/asset_criticality/upload_csv') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * And tags to detection alerts, and remove them from alerts. > info @@ -426,6 +551,24 @@ detection engine rules. .send(props.body as object) .query(props.query); }, + /** + * Calculates and returns a list of Risk Scores, sorted by identifier_type and risk score. + */ + previewRiskScore(props: PreviewRiskScoreProps) { + return supertest + .post('/internal/risk_score/preview') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + readRiskEngineSettings() { + return supertest + .get('/internal/risk_score/engine/settings') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * Retrieve a detection rule using the `rule_id` or `id` field. */ @@ -502,6 +645,17 @@ detection engine rules. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Calculates and persists Risk Scores for an entity, returning the calculated risk score. + */ + triggerRiskScoreCalculation(props: TriggerRiskScoreCalculationProps) { + return supertest + .post('/internal/risk_score/calculation/entity') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Update a detection rule using the `rule_id` or `id` field. The original rule is replaced, and all unspecified fields are deleted. > info @@ -516,6 +670,13 @@ detection engine rules. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + uploadAssetCriticalityRecords() { + return supertest + .post('/api/asset_criticality/upload_csv') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, }; } @@ -537,9 +698,15 @@ export interface BulkPatchRulesProps { export interface BulkUpdateRulesProps { body: BulkUpdateRulesRequestBodyInput; } +export interface BulkUpsertAssetCriticalityRecordsProps { + body: BulkUpsertAssetCriticalityRecordsRequestBodyInput; +} export interface CreateAlertsMigrationProps { body: CreateAlertsMigrationRequestBodyInput; } +export interface CreateAssetCriticalityRecordProps { + body: CreateAssetCriticalityRecordRequestBodyInput; +} export interface CreateRuleProps { body: CreateRuleRequestBodyInput; } @@ -547,9 +714,15 @@ export interface CreateUpdateProtectionUpdatesNoteProps { params: CreateUpdateProtectionUpdatesNoteRequestParamsInput; body: CreateUpdateProtectionUpdatesNoteRequestBodyInput; } +export interface DeleteAssetCriticalityRecordProps { + query: DeleteAssetCriticalityRecordRequestQueryInput; +} export interface DeleteRuleProps { query: DeleteRuleRequestQueryInput; } +export interface DeprecatedTriggerRiskScoreCalculationProps { + body: DeprecatedTriggerRiskScoreCalculationRequestBodyInput; +} export interface EndpointIsolateRedirectProps { body: EndpointIsolateRedirectRequestBodyInput; } @@ -563,6 +736,9 @@ export interface ExportRulesProps { export interface FinalizeAlertsMigrationProps { body: FinalizeAlertsMigrationRequestBodyInput; } +export interface FindAssetCriticalityRecordsProps { + query: FindAssetCriticalityRecordsRequestQueryInput; +} export interface FindRulesProps { query: FindRulesRequestQueryInput; } @@ -572,6 +748,9 @@ export interface GetAgentPolicySummaryProps { export interface GetAlertsMigrationStatusProps { query: GetAlertsMigrationStatusRequestQueryInput; } +export interface GetAssetCriticalityRecordProps { + query: GetAssetCriticalityRecordRequestQueryInput; +} export interface GetEndpointSuggestionsProps { params: GetEndpointSuggestionsRequestParamsInput; body: GetEndpointSuggestionsRequestBodyInput; @@ -593,6 +772,12 @@ export interface GetRuleExecutionResultsProps { export interface ImportRulesProps { query: ImportRulesRequestQueryInput; } +export interface InternalCreateAssetCriticalityRecordProps { + body: InternalCreateAssetCriticalityRecordRequestBodyInput; +} +export interface InternalDeleteAssetCriticalityRecordProps { + query: InternalDeleteAssetCriticalityRecordRequestQueryInput; +} export interface ManageAlertTagsProps { body: ManageAlertTagsRequestBodyInput; } @@ -603,6 +788,9 @@ export interface PerformBulkActionProps { query: PerformBulkActionRequestQueryInput; body: PerformBulkActionRequestBodyInput; } +export interface PreviewRiskScoreProps { + body: PreviewRiskScoreRequestBodyInput; +} export interface ReadRuleProps { query: ReadRuleRequestQueryInput; } @@ -621,6 +809,9 @@ export interface SetAlertsStatusProps { export interface SuggestUserProfilesProps { query: SuggestUserProfilesRequestQueryInput; } +export interface TriggerRiskScoreCalculationProps { + body: TriggerRiskScoreCalculationRequestBodyInput; +} export interface UpdateRuleProps { body: UpdateRuleRequestBodyInput; } 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 133d988f04653..97e53e87ed2f9 100644 --- a/x-pack/test/functional/page_objects/search_playground_page.ts +++ b/x-pack/test/functional/page_objects/search_playground_page.ts @@ -19,7 +19,35 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) await testSubjects.click('saveButton'); }; + const SESSION_KEY = 'search_playground_session'; + return { + session: { + async clearSession(): Promise { + await browser.setLocalStorageItem(SESSION_KEY, '{}'); + }, + + async setSession(): Promise { + await browser.setLocalStorageItem( + SESSION_KEY, + JSON.stringify({ + prompt: 'You are a fireman in london that helps answering question-answering tasks.', + }) + ); + }, + + async expectSession(): Promise { + const session = (await browser.getLocalStorageItem(SESSION_KEY)) || '{}'; + const state = JSON.parse(session); + expect(state.prompt).to.be('You are an assistant for question-answering tasks.'); + expect(state.doc_size).to.be(3); + expect(state.elasticsearch_query).eql({ + retriever: { + standard: { query: { multi_match: { query: '{query}', fields: ['baz'] } } }, + }, + }); + }, + }, PlaygroundStartChatPage: { async expectPlaygroundStartChatPageComponentsToExist() { await testSubjects.existOrFail('setupPage'); @@ -85,6 +113,13 @@ export function SearchPlaygroundPageProvider({ getService }: FtrProviderContext) await testSubjects.existOrFail('chatPage'); }, + async expectPromptToBe(text: string) { + await testSubjects.existOrFail('instructionsPrompt'); + const instructionsPromptElement = await testSubjects.find('instructionsPrompt'); + const promptInstructions = await instructionsPromptElement.getVisibleText(); + expect(promptInstructions).to.contain(text); + }, + async expectChatWindowLoaded() { expect(await testSubjects.getAttribute('viewModeSelector', 'disabled')).to.be(null); expect(await testSubjects.isEnabled('dataSourceActionButton')).to.be(true); diff --git a/x-pack/test/security_solution_api_integration/config/services/security_solution_edr_workflows_roles_users.ts b/x-pack/test/security_solution_api_integration/config/services/security_solution_edr_workflows_roles_users.ts index f364943164322..92e0cc9ba1f13 100644 --- a/x-pack/test/security_solution_api_integration/config/services/security_solution_edr_workflows_roles_users.ts +++ b/x-pack/test/security_solution_api_integration/config/services/security_solution_edr_workflows_roles_users.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { Role } from '@kbn/security-plugin/common'; import { EndpointSecurityRoleNames, ENDPOINT_SECURITY_ROLE_NAMES, @@ -61,9 +62,25 @@ export function RolesUsersProvider({ getService }: FtrProviderContext) { await security.role.create(predefinedRole, roleConfig); } if (customRole) { - await security.role.create(customRole.roleName, { - permissions: { feature: { siem: [...customRole.extraPrivileges] } }, - }); + const role: Omit = { + description: '', + elasticsearch: { + cluster: [], + indices: [], + run_as: [], + }, + kibana: [ + { + spaces: ['*'], + base: [], + feature: { + siem: customRole.extraPrivileges, + }, + }, + ], + }; + + await security.role.create(customRole.roleName, role); } }, diff --git a/x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/execute.ts b/x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/execute.ts index 4178fd80b653a..6e50f67e3510d 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/execute.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/execute.ts @@ -6,14 +6,21 @@ */ import { wrapErrorAndRejectPromise } from '@kbn/security-solution-plugin/common/endpoint/data_loaders/utils'; import expect from '@kbn/expect'; -import { EXECUTE_ROUTE } from '@kbn/security-solution-plugin/common/endpoint/constants'; +import { + ACTION_AGENT_FILE_INFO_ROUTE, + EXECUTE_ROUTE, +} from '@kbn/security-solution-plugin/common/endpoint/constants'; import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data'; +import { ActionDetails } from '@kbn/security-solution-plugin/common/endpoint/types'; +import { getFileDownloadId } from '@kbn/security-solution-plugin/common/endpoint/service/response_actions/get_file_download_id'; import { FtrProviderContext } from '../../../../ftr_provider_context_edr_workflows'; import { ROLE } from '../../../../config/services/security_solution_edr_workflows_roles_users'; export default function ({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const endpointTestResources = getService('endpointTestResources'); + const rolesUsersProvider = getService('rolesUsersProvider'); + // @skipInServerlessMKI - this test uses internal index manipulation in before/after hooks describe('@ess @serverless @skipInServerlessMKI Endpoint `execute` response action', function () { let indexedData: IndexedHostsAndAlertsResponse; @@ -150,5 +157,66 @@ export default function ({ getService }: FtrProviderContext) { expect(data.parameters.command).to.eql('ls -la'); expect(data.parameters.timeout).to.eql(2000); }); + + // Test checks to ensure API works with a custom role + describe('@skipInServerless @skipInServerlessMKI and with minimal authz', () => { + const username = 'execute_limited'; + const password = 'changeme'; + let fileInfoApiRoutePath: string = ''; + + before(async () => { + await rolesUsersProvider.createRole({ + customRole: { + roleName: username, + extraPrivileges: ['minimal_all', 'execute_operations_all'], + }, + }); + await rolesUsersProvider.createUser({ name: username, password, roles: [username] }); + + const { + body: { data }, + } = await supertestWithoutAuth + .post(EXECUTE_ROUTE) + .auth(username, password) + .set('kbn-xsrf', 'true') + .set('Elastic-Api-Version', '2023-10-31') + .send({ endpoint_ids: [agentId], parameters: { command: 'ls -la' } }) + .expect(200); + + const actionDetails = data as ActionDetails; + + fileInfoApiRoutePath = ACTION_AGENT_FILE_INFO_ROUTE.replace('{action_id}', data.id).replace( + '{file_id}', + getFileDownloadId(actionDetails) + ); + }); + + after(async () => { + await rolesUsersProvider.deleteRoles([username]); + await rolesUsersProvider.deleteUsers([username]); + }); + + it('should have access to file info api', async () => { + await supertestWithoutAuth + .get(fileInfoApiRoutePath) + .auth(username, password) + .set('kbn-xsrf', 'true') + .set('Elastic-Api-Version', '2023-10-31') + // We expect 404 because the indexes with the file info don't exist. + // The key here is that we do NOT get a 401 or 403 + .expect(404); + }); + + it('should have access to file download api', async () => { + await supertestWithoutAuth + .get(`${fileInfoApiRoutePath}/download`) + .auth(username, password) + .set('kbn-xsrf', 'true') + .set('Elastic-Api-Version', '2023-10-31') + // We expect 404 because the indexes with the file info don't exist. + // The key here is that we do NOT get a 401 or 403 + .expect(404); + }); + }); }); } diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts index 9ae70f540f897..11343e077eeaf 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts @@ -23,7 +23,7 @@ import { import type { AssetCriticalityRecord, CreateAssetCriticalityRecord, - ListAssetCriticalityQueryParams, + FindAssetCriticalityRecordsRequestQuery, } from '@kbn/security-solution-plugin/common/api/entity_analytics'; import type { Client } from '@elastic/elasticsearch'; import type { ToolingLog } from '@kbn/tooling-log'; @@ -187,7 +187,7 @@ export const assetCriticalityRouteHelpersFactory = ( .expect(expectStatusCode); }, list: async ( - opts: ListAssetCriticalityQueryParams = {}, + opts: FindAssetCriticalityRecordsRequestQuery = {}, { expectStatusCode }: { expectStatusCode: number } = { expectStatusCode: 200 } ) => { const qs = querystring.stringify(opts); diff --git a/x-pack/test/security_solution_api_integration/tsconfig.json b/x-pack/test/security_solution_api_integration/tsconfig.json index 8584cebd03edb..a4b454cb27870 100644 --- a/x-pack/test/security_solution_api_integration/tsconfig.json +++ b/x-pack/test/security_solution_api_integration/tsconfig.json @@ -46,5 +46,6 @@ "@kbn/utility-types", "@kbn/timelines-plugin", "@kbn/dev-cli-runner", + "@kbn/security-plugin", ] } diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts index b1ff2187bc574..9b34220ee75f3 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts @@ -69,6 +69,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { 'enterpriseSearchApplications', 'enterpriseSearchAISearch', 'enterpriseSearchVectorSearch', + 'enterpriseSearchSemanticSearch', 'enterpriseSearchElasticsearch', 'appSearch', 'workplaceSearch', @@ -99,6 +100,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { 'enterpriseSearchApplications', 'enterpriseSearchAISearch', 'enterpriseSearchVectorSearch', + 'enterpriseSearchSemanticSearch', 'enterpriseSearchElasticsearch', 'appSearch', 'observabilityAIAssistant', diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts index fb7b3a112f6de..b2955ade938b1 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts @@ -56,6 +56,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) { 'enterpriseSearchApplications', 'enterpriseSearchAISearch', 'enterpriseSearchVectorSearch', + 'enterpriseSearchSemanticSearch', 'enterpriseSearchElasticsearch', 'appSearch', 'workplaceSearch' @@ -76,6 +77,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) { 'enterpriseSearchApplications', 'enterpriseSearchAISearch', 'enterpriseSearchVectorSearch', + 'enterpriseSearchSemanticSearch', 'enterpriseSearchElasticsearch', 'observabilityAIAssistant', 'appSearch', diff --git a/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts b/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts index a52cb46b77c9f..18693d4699f53 100644 --- a/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts @@ -33,6 +33,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { 'enterpriseSearchApplications', 'enterpriseSearchAISearch', 'enterpriseSearchVectorSearch', + 'enterpriseSearchSemanticSearch', 'enterpriseSearchElasticsearch', 'appSearch', 'workplaceSearch', diff --git a/x-pack/test/ui_capabilities/spaces_only/tests/nav_links.ts b/x-pack/test/ui_capabilities/spaces_only/tests/nav_links.ts index 2778b8e54a13a..f5559940ff812 100644 --- a/x-pack/test/ui_capabilities/spaces_only/tests/nav_links.ts +++ b/x-pack/test/ui_capabilities/spaces_only/tests/nav_links.ts @@ -25,6 +25,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) { 'enterpriseSearchApplications', 'enterpriseSearchAISearch', 'enterpriseSearchVectorSearch', + 'enterpriseSearchSemanticSearch', 'enterpriseSearchElasticsearch', 'appSearch', 'workplaceSearch', 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 index c1ce48215e2e2..49c223c8d1424 100644 --- 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 @@ -40,7 +40,8 @@ export default function (providerContext: FtrProviderContext) { 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 () { + // FLAKY: https://github.com/elastic/kibana/issues/188660 + describe.skip('Intercept the usage API request sent by the metering background task manager', function () { this.tags(['skipMKI']); let mockUsageApiServer: http.Server; 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 2e8538e154cbe..656671f277d75 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 @@ -137,6 +137,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await createConnector(); await createIndex(); await browser.refresh(); + await pageObjects.searchPlayground.session.clearSession(); await pageObjects.searchPlayground.PlaygroundChatPage.navigateToChatPage(); }); it('loads successfully', async () => { @@ -153,6 +154,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ) === undefined ); + await pageObjects.searchPlayground.session.expectSession(); + await pageObjects.searchPlayground.PlaygroundChatPage.sendQuestion(); const conversationSimulator = await conversationInterceptor.waitForIntercept(); @@ -180,6 +183,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('save selected fields between modes', async () => { await pageObjects.searchPlayground.PlaygroundChatPage.expectSaveFieldsBetweenModes(); }); + + it('loads a session from localstorage', async () => { + await pageObjects.searchPlayground.session.setSession(); + await browser.refresh(); + await pageObjects.searchPlayground.PlaygroundChatPage.navigateToChatPage(); + await pageObjects.searchPlayground.PlaygroundChatPage.expectPromptToBe( + 'You are a fireman in london that helps answering question-answering tasks.' + ); + }); }); after(async () => {