From e64d54577ee394948706664b5413c1d38124b82f Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Wed, 15 Feb 2023 09:36:18 +0100 Subject: [PATCH 01/68] register model version migration to the document migrator (#150842) ## Summary Part of https://github.com/elastic/kibana/issues/150301 - Add logic to converts model version transformations to the format used by the document migrator - Use in when preparing migration data for the document migrator - Improve the migration validation logic to take model versions into account (and clean it) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../index.ts | 7 + .../src/model_version/constants.ts | 12 + .../src/model_version/conversion.test.ts | 105 ++++++ .../src/model_version/conversion.ts | 94 +++++ .../src/model_version/index.ts | 15 + .../build_active_migrations.test.mocks.ts | 34 ++ .../build_active_migrations.test.ts | 322 ++++++++++++++++++ .../build_active_migrations.ts | 98 ++++-- .../document_migrator.test.mock.ts | 12 +- .../document_migrator.test.ts | 175 +++------- .../document_migrator/document_migrator.ts | 17 +- .../document_migrator/model_version.test.ts | 187 ++++++++++ .../src/document_migrator/model_version.ts | 84 +++++ .../src/document_migrator/types.ts | 4 +- .../src/document_migrator/utils.ts | 2 +- .../validate_migration.test.ts | 272 +++++++++++++++ .../document_migrator/validate_migrations.ts | 240 ++++++++----- .../tsconfig.json | 1 + .../src/model_version/transformations.ts | 4 +- 19 files changed, 1417 insertions(+), 268 deletions(-) create mode 100644 packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/constants.ts create mode 100644 packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts create mode 100644 packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts create mode 100644 packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts create mode 100644 packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.mocks.ts create mode 100644 packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.ts create mode 100644 packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.test.ts create mode 100644 packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.ts create mode 100644 packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts index b5a85a13121f1..722694a32f6ad 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts @@ -39,3 +39,10 @@ export type { MigrationStatus, } from './src/migration'; export { parseObjectKey, getObjectKey, getIndexForType } from './src/utils'; +export { + modelVersionVirtualMajor, + assertValidModelVersion, + isVirtualModelVersion, + virtualVersionToModelVersion, + modelVersionToVirtualVersion, +} from './src/model_version'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/constants.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/constants.ts new file mode 100644 index 0000000000000..f3f8ace3a142b --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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. + */ + +/** + * The major version that is used to represent model versions. + */ +export const modelVersionVirtualMajor = 10; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts new file mode 100644 index 0000000000000..b65bdbce41b7a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + isVirtualModelVersion, + virtualVersionToModelVersion, + modelVersionToVirtualVersion, + assertValidModelVersion, +} from './conversion'; + +describe('isVirtualModelVersion', () => { + it('returns true when the version is a virtual model version', () => { + expect(isVirtualModelVersion('10.0.0')).toEqual(true); + expect(isVirtualModelVersion('10.7.0')).toEqual(true); + expect(isVirtualModelVersion('10.12.0')).toEqual(true); + }); + + it('returns false when the version is not a virtual model version', () => { + expect(isVirtualModelVersion('9.2.0')).toEqual(false); + expect(isVirtualModelVersion('10.7.1')).toEqual(false); + expect(isVirtualModelVersion('11.2.0')).toEqual(false); + }); + + it('throws when the version is not a valid semver', () => { + expect(() => isVirtualModelVersion('9.-2.0')).toThrowErrorMatchingInlineSnapshot( + `"Invalid semver: 9.-2.0"` + ); + expect(() => isVirtualModelVersion('12.3.5.6.7')).toThrowErrorMatchingInlineSnapshot( + `"Invalid semver: 12.3.5.6.7"` + ); + expect(() => isVirtualModelVersion('dolly')).toThrowErrorMatchingInlineSnapshot( + `"Invalid semver: dolly"` + ); + }); +}); + +describe('virtualVersionToModelVersion', () => { + it('converts the given virtual version to its model version', () => { + expect(virtualVersionToModelVersion('10.0.0')).toEqual(0); + expect(virtualVersionToModelVersion('10.7.0')).toEqual(7); + expect(virtualVersionToModelVersion('10.12.0')).toEqual(12); + }); + + it('throws when the version is not a virtual model version', () => { + expect(() => virtualVersionToModelVersion('9.2.0')).toThrowErrorMatchingInlineSnapshot( + `"Version is not a virtual model version"` + ); + expect(() => virtualVersionToModelVersion('11.3.0')).toThrowErrorMatchingInlineSnapshot( + `"Version is not a virtual model version"` + ); + expect(() => virtualVersionToModelVersion('10.3.42')).toThrowErrorMatchingInlineSnapshot( + `"Version is not a virtual model version"` + ); + }); + + it('throws when the version is not a valid semver', () => { + expect(() => virtualVersionToModelVersion('9.-2.0')).toThrowErrorMatchingInlineSnapshot( + `"Invalid semver: 9.-2.0"` + ); + expect(() => virtualVersionToModelVersion('12.3.5.6.7')).toThrowErrorMatchingInlineSnapshot( + `"Invalid semver: 12.3.5.6.7"` + ); + expect(() => virtualVersionToModelVersion('dolly')).toThrowErrorMatchingInlineSnapshot( + `"Invalid semver: dolly"` + ); + }); +}); + +describe('modelVersionToVirtualVersion', () => { + it('converts the given model version to its virtual version', () => { + expect(modelVersionToVirtualVersion(0)).toEqual('10.0.0'); + expect(modelVersionToVirtualVersion(7)).toEqual('10.7.0'); + expect(modelVersionToVirtualVersion(12)).toEqual('10.12.0'); + }); +}); + +describe('assertValidModelVersion', () => { + it('throws if the provided value is not an integer', () => { + expect(() => assertValidModelVersion(9.4)).toThrowErrorMatchingInlineSnapshot( + `"Model version must be an integer"` + ); + expect(() => assertValidModelVersion('7.6')).toThrowErrorMatchingInlineSnapshot( + `"Model version must be an integer"` + ); + }); + + it('throws if the provided value is a negative integer', () => { + expect(() => assertValidModelVersion(-4)).toThrowErrorMatchingInlineSnapshot( + `"Model version cannot be negative"` + ); + expect(() => assertValidModelVersion('-3')).toThrowErrorMatchingInlineSnapshot( + `"Model version cannot be negative"` + ); + }); + + it('returns the model version as a number', () => { + expect(assertValidModelVersion(4)).toEqual(4); + expect(assertValidModelVersion('3')).toEqual(3); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts new file mode 100644 index 0000000000000..c3765ca3c9be9 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Semver from 'semver'; +import { modelVersionVirtualMajor } from './constants'; + +/** + * Returns the virtual version associated with the given model version + * + * @example + * ``` + * modelVersionToVirtualVersion(5); // "10.5.0"; + * modelVersionToVirtualVersion("3"); // "10.3.0"; + * ``` + */ +export const modelVersionToVirtualVersion = (modelVersion: number | string) => { + const validatedModelVersion = assertValidModelVersion(modelVersion); + return `${modelVersionVirtualMajor}.${validatedModelVersion}.0`; +}; + +/** + * Return true if the given semver version is a virtual model version. + * Virtual model versions are version which major is the {@link modelVersionVirtualMajor} + * + * @example + * ``` + * isVirtualModelVersion("10.3.0"); // true + * isVirtualModelVersion("9.7.0); // false + * isVirtualModelVersion("10.3.1); // false + * ``` + */ +export const isVirtualModelVersion = (version: string): boolean => { + const semver = Semver.parse(version); + if (!semver) { + throw new Error(`Invalid semver: ${version}`); + } + return _isVirtualModelVersion(semver); +}; + +/** + * Converts a virtual model version to its model version. + * + * @example + * ``` + * virtualVersionToModelVersion('10.3.0'); // 3 + * virtualVersionToModelVersion('9.3.0'); // throw + * ``` + */ +export const virtualVersionToModelVersion = (virtualVersion: string): number => { + const semver = Semver.parse(virtualVersion); + if (!semver) { + throw new Error(`Invalid semver: ${virtualVersion}`); + } + if (!_isVirtualModelVersion(semver)) { + throw new Error(`Version is not a virtual model version`); + } + return semver.minor; +}; + +/** + * Asserts the provided number or string is a valid model version, and returns it. + * + * A valid model version is a positive integer. + * + * @example + * ``` + * assertValidModelVersion("7"); // 7 + * assertValidModelVersion(4); // 4 + * assertValidModelVersion("foo"); // throw + * assertValidModelVersion("9.7"); // throw + * assertValidModelVersion("-3"); // throw + * ``` + */ +export const assertValidModelVersion = (modelVersion: string | number): number => { + if (typeof modelVersion === 'string') { + modelVersion = parseFloat(modelVersion); + } + if (!Number.isInteger(modelVersion)) { + throw new Error('Model version must be an integer'); + } + if (modelVersion < 0) { + throw new Error('Model version cannot be negative'); + } + return modelVersion; +}; + +const _isVirtualModelVersion = (semver: Semver.SemVer): boolean => { + return semver.major === modelVersionVirtualMajor && semver.patch === 0; +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts new file mode 100644 index 0000000000000..5301c0a4d219c --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { modelVersionVirtualMajor } from './constants'; +export { + assertValidModelVersion, + isVirtualModelVersion, + modelVersionToVirtualVersion, + virtualVersionToModelVersion, +} from './conversion'; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.mocks.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.mocks.ts new file mode 100644 index 0000000000000..6bf80d0e4b1b4 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.mocks.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 getReferenceTransformsMock = jest.fn(); +export const getConversionTransformsMock = jest.fn(); + +jest.doMock('./internal_transforms', () => ({ + getReferenceTransforms: getReferenceTransformsMock, + getConversionTransforms: getConversionTransformsMock, +})); + +export const getModelVersionTransformsMock = jest.fn(); + +jest.doMock('./model_version', () => ({ + getModelVersionTransforms: getModelVersionTransformsMock, +})); + +export const validateTypeMigrationsMock = jest.fn(); + +jest.doMock('./validate_migrations', () => ({ + validateTypeMigrations: validateTypeMigrationsMock, +})); + +export const resetAllMocks = () => { + getReferenceTransformsMock.mockReset().mockReturnValue([]); + getConversionTransformsMock.mockReset().mockReturnValue([]); + getModelVersionTransformsMock.mockReset().mockReturnValue([]); + validateTypeMigrationsMock.mockReset(); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.ts new file mode 100644 index 0000000000000..0385271ea262d --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.test.ts @@ -0,0 +1,322 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + getConversionTransformsMock, + getModelVersionTransformsMock, + getReferenceTransformsMock, + resetAllMocks, + validateTypeMigrationsMock, +} from './build_active_migrations.test.mocks'; + +import { loggerMock, MockedLogger } from '@kbn/logging-mocks'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { buildActiveMigrations } from './build_active_migrations'; +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; +import { Transform, TransformType } from './types'; + +const kibanaVersion = '3.2.3'; + +describe('buildActiveMigrations', () => { + let log: MockedLogger; + let typeRegistry: SavedObjectTypeRegistry; + + const buildMigrations = () => { + return buildActiveMigrations({ typeRegistry, kibanaVersion, log }); + }; + + const createType = (parts: Partial): SavedObjectsType => ({ + name: 'test', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...parts, + }); + + const transform = (type: TransformType, version: string): Transform => ({ + version, + transformType: type, + transform: jest.fn(), + }); + + const expectTransform = (type: TransformType, version: string): Transform => ({ + version, + transformType: type, + transform: expect.any(Function), + }); + + const addType = (parts: Partial) => { + typeRegistry.registerType(createType(parts)); + }; + + beforeEach(() => { + resetAllMocks(); + + log = loggerMock.create(); + typeRegistry = new SavedObjectTypeRegistry(); + }); + + describe('validation', () => { + it('calls validateMigrationsMapObject with the correct parameters', () => { + addType({ + name: 'foo', + migrations: { + '7.12.0': jest.fn(), + '7.16.0': jest.fn(), + }, + }); + + addType({ + name: 'bar', + migrations: () => ({ + '7.114.0': jest.fn(), + '8.3.0': jest.fn(), + }), + }); + + buildMigrations(); + + expect(validateTypeMigrationsMock).toHaveBeenCalledTimes(2); + expect(validateTypeMigrationsMock).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + type: expect.objectContaining({ name: 'foo' }), + kibanaVersion, + }) + ); + expect(validateTypeMigrationsMock).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + type: expect.objectContaining({ name: 'bar' }), + kibanaVersion, + }) + ); + }); + + it('throws if validateMigrationsMapObject throws', () => { + validateTypeMigrationsMock.mockImplementation(() => { + throw new Error('woups'); + }); + + addType({ + name: 'foo', + migrations: { + '7.12.0': jest.fn(), + '7.16.0': jest.fn(), + }, + }); + + expect(() => buildMigrations()).toThrowErrorMatchingInlineSnapshot(`"woups"`); + }); + }); + + describe('type migrations', () => { + it('returns the migrations registered by the type as transforms', () => { + addType({ + name: 'foo', + migrations: { + '7.12.0': jest.fn(), + '7.16.0': jest.fn(), + '8.3.0': jest.fn(), + }, + }); + + const migrations = buildMigrations(); + + expect(Object.keys(migrations).sort()).toEqual(['foo']); + expect(migrations.foo.transforms).toEqual([ + expectTransform(TransformType.Migrate, '7.12.0'), + expectTransform(TransformType.Migrate, '7.16.0'), + expectTransform(TransformType.Migrate, '8.3.0'), + ]); + }); + }); + + describe('model version transforms', () => { + it('calls getModelVersionTransforms with the correct parameters', () => { + const foo = createType({ name: 'foo' }); + const bar = createType({ name: 'bar' }); + + addType(foo); + addType(bar); + + buildMigrations(); + + expect(getModelVersionTransformsMock).toHaveBeenCalledTimes(2); + expect(getModelVersionTransformsMock).toHaveBeenNthCalledWith(1, { + log, + typeDefinition: foo, + }); + expect(getModelVersionTransformsMock).toHaveBeenNthCalledWith(2, { + log, + typeDefinition: bar, + }); + }); + + it('adds the transform from getModelVersionTransforms to each type', () => { + const foo = createType({ name: 'foo' }); + const bar = createType({ name: 'bar' }); + + addType(foo); + addType(bar); + + getModelVersionTransformsMock.mockImplementation( + ({ typeDefinition }: { typeDefinition: SavedObjectsType }) => { + if (typeDefinition.name === 'foo') { + return [transform(TransformType.Migrate, '7.12.0')]; + } else { + return [transform(TransformType.Migrate, '8.3.0')]; + } + } + ); + + const migrations = buildMigrations(); + + expect(Object.keys(migrations).sort()).toEqual(['bar', 'foo']); + expect(migrations.foo.transforms).toEqual([expectTransform(TransformType.Migrate, '7.12.0')]); + expect(migrations.bar.transforms).toEqual([expectTransform(TransformType.Migrate, '8.3.0')]); + }); + }); + + describe('internal transforms', () => { + it('calls getReferenceTransforms with the correct parameters', () => { + const foo = createType({ name: 'foo' }); + const bar = createType({ name: 'bar' }); + + addType(foo); + addType(bar); + + buildMigrations(); + + expect(getReferenceTransformsMock).toHaveBeenCalledTimes(1); + expect(getReferenceTransformsMock).toHaveBeenCalledWith(typeRegistry); + }); + + it('adds the transform from getReferenceTransforms to each type', () => { + const foo = createType({ name: 'foo' }); + const bar = createType({ name: 'bar' }); + + addType(foo); + addType(bar); + + getReferenceTransformsMock.mockReturnValue([ + transform(TransformType.Reference, '7.12.0'), + transform(TransformType.Reference, '7.17.0'), + ]); + + const migrations = buildMigrations(); + expect(Object.keys(migrations).sort()).toEqual(['bar', 'foo']); + expect(migrations.foo.transforms).toEqual([ + expectTransform(TransformType.Reference, '7.12.0'), + expectTransform(TransformType.Reference, '7.17.0'), + ]); + expect(migrations.bar.transforms).toEqual([ + expectTransform(TransformType.Reference, '7.12.0'), + expectTransform(TransformType.Reference, '7.17.0'), + ]); + }); + + it('calls getConversionTransforms with the correct parameters', () => { + const foo = createType({ name: 'foo' }); + const bar = createType({ name: 'bar' }); + + addType(foo); + addType(bar); + + buildMigrations(); + + expect(getConversionTransformsMock).toHaveBeenCalledTimes(2); + expect(getConversionTransformsMock).toHaveBeenNthCalledWith(1, foo); + expect(getConversionTransformsMock).toHaveBeenNthCalledWith(2, bar); + }); + + it('adds the transform from getConversionTransforms to each type', () => { + const foo = createType({ name: 'foo' }); + const bar = createType({ name: 'bar' }); + + addType(foo); + addType(bar); + + getConversionTransformsMock.mockImplementation((type: SavedObjectsType) => { + if (type.name === 'foo') { + return [transform(TransformType.Convert, '7.12.0')]; + } else { + return [transform(TransformType.Convert, '8.7.0')]; + } + }); + + const migrations = buildMigrations(); + expect(Object.keys(migrations).sort()).toEqual(['bar', 'foo']); + expect(migrations.foo.transforms).toEqual([expectTransform(TransformType.Convert, '7.12.0')]); + expect(migrations.bar.transforms).toEqual([expectTransform(TransformType.Convert, '8.7.0')]); + }); + }); + + describe('ordering', () => { + it('sort the migrations correctly', () => { + addType({ + name: 'foo', + migrations: { + '7.12.0': jest.fn(), + '7.16.0': jest.fn(), + }, + }); + + addType({ + name: 'bar', + migrations: { + '7.17.0': jest.fn(), + '8.2.1': jest.fn(), + }, + }); + + getModelVersionTransformsMock.mockImplementation( + ({ typeDefinition }: { typeDefinition: SavedObjectsType }) => { + if (typeDefinition.name === 'foo') { + return [transform(TransformType.Migrate, '7.18.2')]; + } else { + return [transform(TransformType.Migrate, '8.4.2')]; + } + } + ); + + getReferenceTransformsMock.mockReturnValue([ + transform(TransformType.Reference, '7.12.0'), + transform(TransformType.Reference, '7.17.3'), + ]); + + getConversionTransformsMock.mockImplementation((type: SavedObjectsType) => { + if (type.name === 'foo') { + return [transform(TransformType.Convert, '7.14.0')]; + } else { + return [transform(TransformType.Convert, '8.7.0')]; + } + }); + + const migrations = buildMigrations(); + + expect(Object.keys(migrations).sort()).toEqual(['bar', 'foo']); + expect(migrations.foo.transforms).toEqual([ + expectTransform(TransformType.Reference, '7.12.0'), + expectTransform(TransformType.Migrate, '7.12.0'), + expectTransform(TransformType.Convert, '7.14.0'), + expectTransform(TransformType.Migrate, '7.16.0'), + expectTransform(TransformType.Reference, '7.17.3'), + expectTransform(TransformType.Migrate, '7.18.2'), + ]); + expect(migrations.bar.transforms).toEqual([ + expectTransform(TransformType.Reference, '7.12.0'), + expectTransform(TransformType.Migrate, '7.17.0'), + expectTransform(TransformType.Reference, '7.17.3'), + expectTransform(TransformType.Migrate, '8.2.1'), + expectTransform(TransformType.Migrate, '8.4.2'), + expectTransform(TransformType.Convert, '8.7.0'), + ]); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.ts index 51d951a2a9a00..b14e2d237c8dd 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/build_active_migrations.ts @@ -8,11 +8,12 @@ import _ from 'lodash'; import type { Logger } from '@kbn/logging'; -import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import { type ActiveMigrations, type Transform, TransformType } from './types'; +import type { ISavedObjectTypeRegistry, SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { type ActiveMigrations, type Transform, type TypeTransforms, TransformType } from './types'; import { getReferenceTransforms, getConversionTransforms } from './internal_transforms'; -import { validateMigrationsMapObject } from './validate_migrations'; -import { transformComparator, wrapWithTry } from './utils'; +import { validateTypeMigrations } from './validate_migrations'; +import { transformComparator, convertMigrationFunction } from './utils'; +import { getModelVersionTransforms } from './model_version'; /** * Converts migrations from a format that is convenient for callers to a format that @@ -20,45 +21,76 @@ import { transformComparator, wrapWithTry } from './utils'; * From: { type: { version: fn } } * To: { type: { latestVersion?: Record; transforms: [{ version: string, transform: fn }] } } */ -export function buildActiveMigrations( - typeRegistry: ISavedObjectTypeRegistry, - kibanaVersion: string, - log: Logger -): ActiveMigrations { +export function buildActiveMigrations({ + typeRegistry, + kibanaVersion, + convertVersion, + log, +}: { + typeRegistry: ISavedObjectTypeRegistry; + kibanaVersion: string; + convertVersion?: string; + log: Logger; +}): ActiveMigrations { const referenceTransforms = getReferenceTransforms(typeRegistry); return typeRegistry.getAllTypes().reduce((migrations, type) => { - const migrationsMap = - typeof type.migrations === 'function' ? type.migrations() : type.migrations; - validateMigrationsMapObject(type.name, kibanaVersion, migrationsMap); + validateTypeMigrations({ type, kibanaVersion, convertVersion }); - const migrationTransforms = Object.entries(migrationsMap ?? {}).map( - ([version, transform]) => ({ - version, - transform: wrapWithTry(version, type, transform, log), - transformType: TransformType.Migrate, - }) - ); - const conversionTransforms = getConversionTransforms(type); - const transforms = [ - ...referenceTransforms, - ...conversionTransforms, - ...migrationTransforms, - ].sort(transformComparator); + const typeTransforms = buildTypeTransforms({ + type, + log, + kibanaVersion, + referenceTransforms, + }); - if (!transforms.length) { + if (!typeTransforms.transforms.length) { return migrations; } return { ...migrations, - [type.name]: { - latestVersion: _.chain(transforms) - .groupBy('transformType') - .mapValues((items) => _.last(items)?.version) - .value() as Record, - transforms, - }, + [type.name]: typeTransforms, }; }, {} as ActiveMigrations); } + +const buildTypeTransforms = ({ + type, + log, + referenceTransforms, +}: { + type: SavedObjectsType; + kibanaVersion: string; + log: Logger; + referenceTransforms: Transform[]; +}): TypeTransforms => { + const migrationsMap = + typeof type.migrations === 'function' ? type.migrations() : type.migrations ?? {}; + + const migrationTransforms = Object.entries(migrationsMap ?? {}).map( + ([version, transform]) => ({ + version, + transform: convertMigrationFunction(version, type, transform, log), + transformType: TransformType.Migrate, + }) + ); + + const modelVersionTransforms = getModelVersionTransforms({ log, typeDefinition: type }); + + const conversionTransforms = getConversionTransforms(type); + const transforms = [ + ...referenceTransforms, + ...conversionTransforms, + ...migrationTransforms, + ...modelVersionTransforms, + ].sort(transformComparator); + + return { + latestVersion: _.chain(transforms) + .groupBy('transformType') + .mapValues((items) => _.last(items)?.version) + .value() as Record, + transforms, + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.mock.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.mock.ts index df7914a55876f..34137133907b2 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.mock.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -const mockGetConvertedObjectId = jest.fn().mockReturnValue('uuidv5'); +export const mockGetConvertedObjectId = jest.fn().mockReturnValue('uuidv5'); jest.mock('@kbn/core-saved-objects-utils-server', () => { const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); @@ -19,4 +19,12 @@ jest.mock('@kbn/core-saved-objects-utils-server', () => { }; }); -export { mockGetConvertedObjectId }; +export const validateTypeMigrationsMock = jest.fn(); + +jest.doMock('./validate_migrations', () => { + const actual = jest.requireActual('./validate_migrations'); + return { + ...actual, + validateTypeMigrations: validateTypeMigrationsMock, + }; +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts index 2d7fb11587840..677c9806e462b 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -import { mockGetConvertedObjectId } from './document_migrator.test.mock'; +import { + mockGetConvertedObjectId, + validateTypeMigrationsMock, +} from './document_migrator.test.mock'; import { set } from '@kbn/safer-lodash-set'; import _ from 'lodash'; import type { SavedObjectUnsanitizedDoc, SavedObjectsType } from '@kbn/core-saved-objects-server'; @@ -48,6 +51,7 @@ const createRegistry = (...types: Array>) => { beforeEach(() => { mockGetConvertedObjectId.mockClear(); + validateTypeMigrationsMock.mockReset(); }); describe('DocumentMigrator', () => { @@ -60,69 +64,47 @@ describe('DocumentMigrator', () => { } describe('validation', () => { - const createDefinition = (migrations: any) => ({ - kibanaVersion: '3.2.3', - convertVersion: '8.0.0', - typeRegistry: createRegistry({ - name: 'foo', - migrations: migrations as any, - }), - log: mockLogger, - }); - - describe('#prepareMigrations', () => { - it('validates individual migration definitions', () => { - const invalidMigrator = new DocumentMigrator(createDefinition(() => 123)); - const voidMigrator = new DocumentMigrator(createDefinition(() => {})); - const emptyObjectMigrator = new DocumentMigrator(createDefinition(() => ({}))); - - expect(invalidMigrator.prepareMigrations).toThrow( - /Migrations map for type foo should be an object/i - ); - expect(voidMigrator.prepareMigrations).not.toThrow(); - expect(emptyObjectMigrator.prepareMigrations).not.toThrow(); - }); - - it('validates individual migrations are valid semvers', () => { - const withInvalidVersion = { - bar: (doc: any) => doc, - '1.2.3': (doc: any) => doc, + describe('during #prepareMigrations', () => { + it('calls validateMigrationsMapObject with the correct parameters', () => { + const ops = { + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'foo', + migrations: { + '1.2.3': setAttr('attributes.name', 'Chris'), + }, + }, + { + name: 'bar', + migrations: { + '1.2.3': setAttr('attributes.name', 'Chris'), + }, + } + ), }; - const migrationFn = new DocumentMigrator(createDefinition(() => withInvalidVersion)); - const migrationObj = new DocumentMigrator(createDefinition(withInvalidVersion)); - - expect(migrationFn.prepareMigrations).toThrow(/Expected all properties to be semvers/i); - expect(migrationObj.prepareMigrations).toThrow(/Expected all properties to be semvers/i); - }); - it('validates individual migrations are not greater than the current Kibana version', () => { - const withGreaterVersion = { - '3.2.4': (doc: any) => doc, - }; - const migrationFn = new DocumentMigrator(createDefinition(() => withGreaterVersion)); - const migrationObj = new DocumentMigrator(createDefinition(withGreaterVersion)); + const migrator = new DocumentMigrator(ops); - const expectedError = `Invalid migration for type foo. Property '3.2.4' cannot be greater than the current Kibana version '3.2.3'.`; - expect(migrationFn.prepareMigrations).toThrowError(expectedError); - expect(migrationObj.prepareMigrations).toThrowError(expectedError); - }); + expect(validateTypeMigrationsMock).not.toHaveBeenCalled(); - it('validates the migration function', () => { - const invalidVersionFunction = { '1.2.3': 23 as any }; - const migrationFn = new DocumentMigrator(createDefinition(() => invalidVersionFunction)); - const migrationObj = new DocumentMigrator(createDefinition(invalidVersionFunction)); + migrator.prepareMigrations(); - expect(migrationFn.prepareMigrations).toThrow(/expected a function, but got 23/i); - expect(migrationObj.prepareMigrations).toThrow(/expected a function, but got 23/i); - }); - it('validates definitions with migrations: Function | Objects', () => { - const validMigrationMap = { - '1.2.3': () => {}, - }; - const migrationFn = new DocumentMigrator(createDefinition(() => validMigrationMap)); - const migrationObj = new DocumentMigrator(createDefinition(validMigrationMap)); - expect(migrationFn.prepareMigrations).not.toThrow(); - expect(migrationObj.prepareMigrations).not.toThrow(); + expect(validateTypeMigrationsMock).toHaveBeenCalledTimes(3); + expect(validateTypeMigrationsMock).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + type: expect.objectContaining({ name: 'foo' }), + kibanaVersion, + }) + ); + expect(validateTypeMigrationsMock).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + type: expect.objectContaining({ name: 'bar' }), + kibanaVersion, + }) + ); }); }); @@ -155,81 +137,6 @@ describe('DocumentMigrator', () => { }) ).toThrow(/Migrations are not ready. Make sure prepareMigrations is called first./i); }); - - it(`validates convertToMultiNamespaceTypeVersion can only be used with namespaceType 'multiple' or 'multiple-isolated'`, () => { - const invalidDefinition = { - kibanaVersion: '3.2.3', - typeRegistry: createRegistry({ - name: 'foo', - convertToMultiNamespaceTypeVersion: 'bar', - }), - log: mockLogger, - }; - expect(() => new DocumentMigrator(invalidDefinition)).toThrow( - `Invalid convertToMultiNamespaceTypeVersion for type foo. Expected namespaceType to be 'multiple' or 'multiple-isolated', but got 'single'.` - ); - }); - - it(`validates convertToMultiNamespaceTypeVersion must be a semver`, () => { - const invalidDefinition = { - kibanaVersion: '3.2.3', - typeRegistry: createRegistry({ - name: 'foo', - convertToMultiNamespaceTypeVersion: 'bar', - namespaceType: 'multiple', - }), - log: mockLogger, - }; - expect(() => new DocumentMigrator(invalidDefinition)).toThrow( - `Invalid convertToMultiNamespaceTypeVersion for type foo. Expected value to be a semver, but got 'bar'.` - ); - }); - - it('validates convertToMultiNamespaceTypeVersion matches the convertVersion, if specified', () => { - const invalidDefinition = { - kibanaVersion: '3.2.3', - typeRegistry: createRegistry({ - name: 'foo', - convertToMultiNamespaceTypeVersion: '3.2.4', - namespaceType: 'multiple', - }), - convertVersion: '3.2.3', - log: mockLogger, - }; - expect(() => new DocumentMigrator(invalidDefinition)).toThrowError( - `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be any other than '3.2.3'.` - ); - }); - - it('validates convertToMultiNamespaceTypeVersion is not greater than the current Kibana version', () => { - const invalidDefinition = { - kibanaVersion: '3.2.3', - typeRegistry: createRegistry({ - name: 'foo', - convertToMultiNamespaceTypeVersion: '3.2.4', - namespaceType: 'multiple', - }), - log: mockLogger, - }; - expect(() => new DocumentMigrator(invalidDefinition)).toThrowError( - `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be greater than the current Kibana version '3.2.3'.` - ); - }); - - it('validates convertToMultiNamespaceTypeVersion is not used on a patch version', () => { - const invalidDefinition = { - kibanaVersion: '3.2.3', - typeRegistry: createRegistry({ - name: 'foo', - convertToMultiNamespaceTypeVersion: '3.1.1', - namespaceType: 'multiple', - }), - log: mockLogger, - }; - expect(() => new DocumentMigrator(invalidDefinition)).toThrowError( - `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.1.1' cannot be used on a patch version (must be like 'x.y.0').` - ); - }); }); describe('migration', () => { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts index 7dc2e0dd885d5..ada644dcfc9de 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.ts @@ -54,7 +54,6 @@ import type { import type { ActiveMigrations, TransformResult } from './types'; import { maxVersion } from './utils'; import { buildActiveMigrations } from './build_active_migrations'; -import { validateMigrationDefinition } from './validate_migrations'; export type MigrateFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc; export type MigrateAndConvertFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc[]; @@ -89,7 +88,7 @@ export interface VersionedTransformer { * A concrete implementation of the VersionedTransformer interface. */ export class DocumentMigrator implements VersionedTransformer { - private documentMigratorOptions: Omit; + private documentMigratorOptions: DocumentMigratorOptions; private migrations?: ActiveMigrations; private transformDoc?: ApplyTransformsFn; @@ -103,9 +102,8 @@ export class DocumentMigrator implements VersionedTransformer { * @prop {Logger} log - The migration logger * @memberof DocumentMigrator */ - constructor({ typeRegistry, kibanaVersion, convertVersion, log }: DocumentMigratorOptions) { - validateMigrationDefinition(typeRegistry, kibanaVersion, convertVersion); - this.documentMigratorOptions = { typeRegistry, kibanaVersion, log }; + constructor(documentMigratorOptions: DocumentMigratorOptions) { + this.documentMigratorOptions = documentMigratorOptions; } /** @@ -138,8 +136,13 @@ export class DocumentMigrator implements VersionedTransformer { */ public prepareMigrations = () => { - const { typeRegistry, kibanaVersion, log } = this.documentMigratorOptions; - this.migrations = buildActiveMigrations(typeRegistry, kibanaVersion, log); + const { typeRegistry, kibanaVersion, log, convertVersion } = this.documentMigratorOptions; + this.migrations = buildActiveMigrations({ + typeRegistry, + kibanaVersion, + log, + convertVersion, + }); this.transformDoc = buildDocumentTransform({ kibanaVersion, migrations: this.migrations, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.test.ts new file mode 100644 index 0000000000000..c32d18463083c --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.test.ts @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { loggerMock, MockedLogger } from '@kbn/logging-mocks'; +import type { + SavedObjectsType, + SavedObjectsModelVersion, + SavedObjectModelTransformationFn, + SavedObjectUnsanitizedDoc, +} from '@kbn/core-saved-objects-server'; +import { Transform, TransformType } from './types'; +import { getModelVersionTransforms, convertModelVersionTransformFn } from './model_version'; + +describe('getModelVersionTransforms', () => { + let log: MockedLogger; + + const expectTransform = (type: TransformType, version: string): Transform => ({ + version, + transformType: type, + transform: expect.any(Function), + }); + + const createType = (parts: Partial): SavedObjectsType => ({ + name: 'test', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...parts, + }); + + beforeEach(() => { + log = loggerMock.create(); + }); + + it('generate transforms for model version having a transformation', () => { + const typeDefinition = createType({ + name: 'foo', + modelVersions: { + '1': { + modelChange: { + type: 'expansion', + transformation: { up: jest.fn(), down: jest.fn() }, + }, + }, + '2': { + modelChange: { + type: 'expansion', + addedMappings: { foo: { type: 'keyword' } }, + }, + }, + '3': { + modelChange: { + type: 'expansion', + transformation: { up: jest.fn(), down: jest.fn() }, + }, + }, + }, + }); + + const transforms = getModelVersionTransforms({ log, typeDefinition }); + + expect(transforms).toEqual([ + expectTransform(TransformType.Migrate, '10.1.0'), + expectTransform(TransformType.Migrate, '10.3.0'), + ]); + }); + + it('accepts provider functions', () => { + const typeDefinition = createType({ + name: 'foo', + modelVersions: () => ({ + '1': { + modelChange: { + type: 'expansion', + transformation: { up: jest.fn(), down: jest.fn() }, + }, + }, + '2': { + modelChange: { + type: 'expansion', + addedMappings: { foo: { type: 'keyword' } }, + }, + }, + '3': { + modelChange: { + type: 'expansion', + transformation: { up: jest.fn(), down: jest.fn() }, + }, + }, + }), + }); + + const transforms = getModelVersionTransforms({ log, typeDefinition }); + + expect(transforms).toEqual([ + expectTransform(TransformType.Migrate, '10.1.0'), + expectTransform(TransformType.Migrate, '10.3.0'), + ]); + }); +}); + +describe('convertModelVersionTransformFn', () => { + let log: MockedLogger; + let i = 0; + + beforeEach(() => { + i = 0; + log = loggerMock.create(); + }); + + const createDoc = (): SavedObjectUnsanitizedDoc => { + return { type: 'foo', id: `foo-${i++}`, attributes: {} }; + }; + + const createModelTransformFn = (): jest.MockedFunction => { + return jest.fn().mockImplementation((doc: unknown) => ({ + document: doc, + })); + }; + + it('generates a transform function calling the model transform', () => { + const upTransform = createModelTransformFn(); + const downTransform = createModelTransformFn(); + + const definition: SavedObjectsModelVersion = { + modelChange: { + type: 'expansion', + transformation: { up: upTransform, down: downTransform }, + }, + }; + + const transform = convertModelVersionTransformFn({ + log, + modelVersion: 1, + virtualVersion: '10.1.0', + definition, + }); + + expect(upTransform).not.toHaveBeenCalled(); + expect(downTransform).not.toHaveBeenCalled(); + + const doc = createDoc(); + const context = { log, modelVersion: 1 }; + + transform(doc); + + expect(upTransform).toHaveBeenCalledTimes(1); + expect(downTransform).not.toHaveBeenCalled(); + expect(upTransform).toHaveBeenCalledWith(doc, context); + }); + + it('returns the document from the model transform', () => { + const upTransform = createModelTransformFn(); + + const resultDoc = createDoc(); + upTransform.mockImplementation((doc) => { + return { document: resultDoc }; + }); + + const definition: SavedObjectsModelVersion = { + modelChange: { + type: 'expansion', + transformation: { up: upTransform, down: jest.fn() }, + }, + }; + + const transform = convertModelVersionTransformFn({ + log, + modelVersion: 1, + virtualVersion: '10.1.0', + definition, + }); + + const doc = createDoc(); + + const result = transform(doc); + expect(result).toEqual({ + transformedDoc: resultDoc, + additionalDocs: [], + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.ts new file mode 100644 index 0000000000000..cf6f14e2dc83a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/model_version.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Logger } from '@kbn/logging'; +import type { + SavedObjectsType, + SavedObjectsModelVersion, + SavedObjectModelTransformationContext, + SavedObjectUnsanitizedDoc, +} from '@kbn/core-saved-objects-server'; +import { + modelVersionToVirtualVersion, + assertValidModelVersion, +} from '@kbn/core-saved-objects-base-server-internal'; +import { TransformSavedObjectDocumentError } from '../core'; +import { type Transform, type TransformFn, TransformType } from './types'; + +export const getModelVersionTransforms = ({ + typeDefinition, + log, +}: { + typeDefinition: SavedObjectsType; + log: Logger; +}): Transform[] => { + const modelVersionMap = + typeof typeDefinition.modelVersions === 'function' + ? typeDefinition.modelVersions() + : typeDefinition.modelVersions ?? {}; + + const transforms = Object.entries(modelVersionMap) + .filter(([_, definition]) => !!definition.modelChange.transformation) + .map(([rawModelVersion, definition]) => { + const modelVersion = assertValidModelVersion(rawModelVersion); + const virtualVersion = modelVersionToVirtualVersion(modelVersion); + return { + version: virtualVersion, + transform: convertModelVersionTransformFn({ + log, + modelVersion, + virtualVersion, + definition, + }), + transformType: TransformType.Migrate, + }; + }); + + return transforms; +}; + +export const convertModelVersionTransformFn = ({ + virtualVersion, + modelVersion, + definition, + log, +}: { + virtualVersion: string; + modelVersion: number; + definition: SavedObjectsModelVersion; + log: Logger; +}): TransformFn => { + if (!definition.modelChange.transformation) { + throw new Error('cannot convert model change without a transform'); + } + const context: SavedObjectModelTransformationContext = { + log, + modelVersion, + }; + const modelTransformFn = definition.modelChange.transformation.up; + + return function convertedTransform(doc: SavedObjectUnsanitizedDoc) { + try { + const result = modelTransformFn(doc, context); + return { transformedDoc: result.document, additionalDocs: [] }; + } catch (error) { + log.error(error); + throw new TransformSavedObjectDocumentError(error, virtualVersion); + } + }; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/types.ts index f22a010bdb0e8..93e47aa84e673 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/types.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/types.ts @@ -12,13 +12,13 @@ import type { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server'; * Map containing all the info to convert types */ export interface ActiveMigrations { - [type: string]: TypeConversion; + [type: string]: TypeTransforms; } /** * Structure containing all the required info to perform a type's conversion */ -export interface TypeConversion { +export interface TypeTransforms { /** Derived from the related transforms */ latestVersion: Record; /** List of transforms registered for the type **/ diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/utils.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/utils.ts index 3c2b8d83652a9..09aba35da06a1 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/utils.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/utils.ts @@ -21,7 +21,7 @@ import { type Transform, type TransformFn, TransformType } from './types'; * If a specific transform function fails, this tacks on a bit of information * about the document and transform that caused the failure. */ -export function wrapWithTry( +export function convertMigrationFunction( version: string, type: SavedObjectsType, migrationFn: SavedObjectMigrationFn, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts new file mode 100644 index 0000000000000..ef6139572baf6 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts @@ -0,0 +1,272 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectsType, SavedObjectsModelVersion } from '@kbn/core-saved-objects-server'; +import { validateTypeMigrations } from './validate_migrations'; + +describe('validateTypeMigrations', () => { + const defaultKibanaVersion = '3.2.3'; + const defaultConvertVersion = '8.0.0'; + + const someModelVersion: SavedObjectsModelVersion = { + modelChange: { + type: 'expansion', + transformation: { up: jest.fn(), down: jest.fn() }, + }, + }; + + const createType = (parts: Partial): SavedObjectsType => ({ + name: 'test', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, + ...parts, + }); + + const validate = ({ + type, + kibanaVersion = defaultKibanaVersion, + convertVersion = defaultConvertVersion, + }: { + type: SavedObjectsType; + convertVersion?: string; + kibanaVersion?: string; + }) => validateTypeMigrations({ type, kibanaVersion, convertVersion }); + + describe('migrations', () => { + it('validates individual migrations are valid semvers', () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.1.1', + namespaceType: 'multiple', + migrations: { + bar: jest.fn(), + '1.2.3': jest.fn(), + }, + }); + + expect(() => validate({ type })).toThrow(/Expected all properties to be semvers/i); + }); + + it('validates individual migrations are not greater than the current Kibana version', () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.1.1', + namespaceType: 'multiple', + migrations: { + '3.2.4': (doc) => doc, + }, + }); + + expect(() => validate({ type })).toThrowError( + `Invalid migration for type foo. Property '3.2.4' cannot be greater than the current Kibana version '3.2.3'.` + ); + }); + + it('validates the migration function', () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.1.1', + namespaceType: 'multiple', + migrations: { '1.2.3': 23 as any }, + }); + + expect(() => validate({ type })).toThrow(/expected a function, but got 23/i); + }); + + describe('when switchToModelVersionAt is specified', () => { + it('throws if a migration is specified for a version superior to switchToModelVersionAt', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: '8.9.0', + migrations: { + '8.10.0': jest.fn(), + }, + }); + + expect(() => + validate({ type, kibanaVersion: '8.10.0' }) + ).toThrowErrorMatchingInlineSnapshot( + `"Migration for type foo for version 8.10.0 registered after switchToModelVersionAt (8.9.0)"` + ); + }); + it('throws if a migration is specified for a version equal to switchToModelVersionAt', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: '8.9.0', + migrations: { + '8.9.0': jest.fn(), + }, + }); + + expect(() => + validate({ type, kibanaVersion: '8.10.0' }) + ).toThrowErrorMatchingInlineSnapshot( + `"Migration for type foo for version 8.9.0 registered after switchToModelVersionAt (8.9.0)"` + ); + }); + + it('does not throw if a migration is specified for a version inferior to switchToModelVersionAt', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: '8.9.0', + migrations: { + '8.7.0': jest.fn(), + }, + }); + + expect(() => validate({ type, kibanaVersion: '8.10.0' })).not.toThrow(); + }); + }); + }); + + describe('switchToModelVersionAt', () => { + it('throws if the specified version is not a valid semver', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: 'foo', + }); + + expect(() => validate({ type })).toThrowErrorMatchingInlineSnapshot( + `"Type foo: invalid version specified for switchToModelVersionAt: foo"` + ); + }); + + it('throws if the specified version defines a patch version > 0', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: '8.9.3', + }); + + expect(() => validate({ type })).toThrowErrorMatchingInlineSnapshot( + `"Type foo: can't use a patch version for switchToModelVersionAt"` + ); + }); + }); + + describe('modelVersions', () => { + it('throws if used without specifying switchToModelVersionAt', () => { + const type = createType({ + name: 'foo', + modelVersions: { + '1': someModelVersion, + }, + }); + + expect(() => validate({ type, kibanaVersion: '3.2.3' })).toThrowErrorMatchingInlineSnapshot( + `"Type foo: Uusing modelVersions requires to specify switchToModelVersionAt"` + ); + }); + + it('throws if the version number is invalid', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: '3.1.0', + modelVersions: { + '1.1': someModelVersion, + }, + }); + + expect(() => validate({ type, kibanaVersion: '3.2.3' })).toThrowErrorMatchingInlineSnapshot( + `"Model version must be an integer"` + ); + }); + + it('throws when starting with a version higher than 1', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: '3.1.0', + modelVersions: { + '2': someModelVersion, + }, + }); + + expect(() => validate({ type, kibanaVersion: '3.2.3' })).toThrowErrorMatchingInlineSnapshot( + `"Type foo: model versioning must start with version 1"` + ); + }); + + it('throws when there is a gap in versions', () => { + const type = createType({ + name: 'foo', + switchToModelVersionAt: '3.1.0', + modelVersions: { + '1': someModelVersion, + '3': someModelVersion, + '6': someModelVersion, + }, + }); + + expect(() => validate({ type, kibanaVersion: '3.2.3' })).toThrowErrorMatchingInlineSnapshot( + `"Type foo: gaps between model versions aren't allowed (missing versions: 2,4,5)"` + ); + }); + }); + + describe('convertToMultiNamespaceTypeVersion', () => { + it(`validates convertToMultiNamespaceTypeVersion can only be used with namespaceType 'multiple' or 'multiple-isolated'`, () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: 'bar', + }); + expect(() => validate({ type })).toThrow( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Expected namespaceType to be 'multiple' or 'multiple-isolated', but got 'single'.` + ); + }); + + it(`validates convertToMultiNamespaceTypeVersion must be a semver`, () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: 'bar', + namespaceType: 'multiple', + }); + expect(() => validate({ type })).toThrow( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Expected value to be a semver, but got 'bar'.` + ); + }); + + it('validates convertToMultiNamespaceTypeVersion matches the convertVersion, if specified', () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.2.4', + namespaceType: 'multiple', + }); + expect(() => + validate({ type, convertVersion: '3.2.3', kibanaVersion: '3.2.3' }) + ).toThrowError( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be any other than '3.2.3'.` + ); + }); + + it('validates convertToMultiNamespaceTypeVersion is not greater than the current Kibana version', () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.2.4', + namespaceType: 'multiple', + }); + expect(() => + validateTypeMigrations({ type, kibanaVersion: defaultKibanaVersion }) + ).toThrowError( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be greater than the current Kibana version '3.2.3'.` + ); + }); + + it('validates convertToMultiNamespaceTypeVersion is not used on a patch version', () => { + const type = createType({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.1.1', + namespaceType: 'multiple', + }); + expect(() => + validateTypeMigrations({ type, kibanaVersion: defaultKibanaVersion }) + ).toThrowError( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.1.1' cannot be used on a patch version (must be like 'x.y.0').` + ); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts index 463ee85ac1808..2dc93f0e54353 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts @@ -8,114 +8,180 @@ import Semver from 'semver'; import type { SavedObjectsNamespaceType } from '@kbn/core-saved-objects-common'; -import type { - SavedObjectMigrationMap, - ISavedObjectTypeRegistry, -} from '@kbn/core-saved-objects-server'; +import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { assertValidModelVersion } from '@kbn/core-saved-objects-base-server-internal'; /** - * Basic validation that the migration definition matches our expectations. We can't - * rely on TypeScript here, as the caller may be JavaScript / ClojureScript / any compile-to-js - * language. So, this is just to provide a little developer-friendly error messaging. Joi was - * giving weird errors, so we're just doing manual validation. + * Validates the consistency of the given type for use with the document migrator. */ -export function validateMigrationDefinition( - registry: ISavedObjectTypeRegistry, - kibanaVersion: string, - convertVersion?: string -) { - function assertObjectOrFunction(entity: any, prefix: string) { - if (!entity || (typeof entity !== 'function' && typeof entity !== 'object')) { - throw new Error(`${prefix} Got! ${typeof entity}, ${JSON.stringify(entity)}.`); - } - } - - function assertValidConvertToMultiNamespaceType( - namespaceType: SavedObjectsNamespaceType, - convertToMultiNamespaceTypeVersion: string, - type: string - ) { - if (namespaceType !== 'multiple' && namespaceType !== 'multiple-isolated') { - throw new Error( - `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Expected namespaceType to be 'multiple' or 'multiple-isolated', but got '${namespaceType}'.` - ); - } else if (!Semver.valid(convertToMultiNamespaceTypeVersion)) { - throw new Error( - `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Expected value to be a semver, but got '${convertToMultiNamespaceTypeVersion}'.` - ); - } else if (convertVersion && Semver.neq(convertToMultiNamespaceTypeVersion, convertVersion)) { +export function validateTypeMigrations({ + type, + kibanaVersion, + convertVersion, +}: { + type: SavedObjectsType; + kibanaVersion: string; + convertVersion?: string; +}) { + if (type.switchToModelVersionAt) { + const switchToModelVersionAt = Semver.parse(type.switchToModelVersionAt); + if (!switchToModelVersionAt) { throw new Error( - `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be any other than '${convertVersion}'.` - ); - } else if (Semver.gt(convertToMultiNamespaceTypeVersion, kibanaVersion)) { - throw new Error( - `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be greater than the current Kibana version '${kibanaVersion}'.` - ); - } else if (Semver.patch(convertToMultiNamespaceTypeVersion)) { - throw new Error( - `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be used on a patch version (must be like 'x.y.0').` + `Type ${type.name}: invalid version specified for switchToModelVersionAt: ${type.switchToModelVersionAt}` ); } + if (switchToModelVersionAt.patch !== 0) { + throw new Error(`Type ${type.name}: can't use a patch version for switchToModelVersionAt`); + } } - registry.getAllTypes().forEach((type) => { - const { name, migrations, convertToMultiNamespaceTypeVersion, namespaceType } = type; - if (migrations) { - assertObjectOrFunction( - type.migrations, - `Migration for type ${name} should be an object or a function returning an object like { '2.0.0': (doc) => doc }.` - ); - } - if (convertToMultiNamespaceTypeVersion) { - // CHECKPOINT 1 - assertValidConvertToMultiNamespaceType( - namespaceType, - convertToMultiNamespaceTypeVersion, - name - ); - } - }); -} + if (type.migrations) { + assertObjectOrFunction( + type.migrations, + `Migration for type ${type.name} should be an object or a function returning an object like { '2.0.0': (doc) => doc }.` + ); -export function validateMigrationsMapObject( - name: string, - kibanaVersion: string, - migrationsMap?: SavedObjectMigrationMap -) { - function assertObject(obj: any, prefix: string) { - if (!obj || typeof obj !== 'object') { - throw new Error(`${prefix} Got ${obj}.`); - } + const migrationMap = + typeof type.migrations === 'function' ? type.migrations() : type.migrations ?? {}; + + assertObject( + migrationMap, + `Migrations map for type ${type.name} should be an object like { '2.0.0': (doc) => doc }.` + ); + + Object.entries(migrationMap).forEach(([version, fn]) => { + assertValidSemver(kibanaVersion, version, type.name); + assertValidTransform(fn, version, type.name); + if (type.switchToModelVersionAt && Semver.gte(version, type.switchToModelVersionAt)) { + throw new Error( + `Migration for type ${type.name} for version ${version} registered after switchToModelVersionAt (${type.switchToModelVersionAt})` + ); + } + }); } - function assertValidSemver(version: string, type: string) { - if (!Semver.valid(version)) { + if (type.modelVersions) { + const modelVersionMap = + typeof type.modelVersions === 'function' ? type.modelVersions() : type.modelVersions ?? {}; + + if (Object.keys(modelVersionMap).length > 0 && !type.switchToModelVersionAt) { throw new Error( - `Invalid migration for type ${type}. Expected all properties to be semvers, but got ${version}.` + `Type ${type.name}: Uusing modelVersions requires to specify switchToModelVersionAt` ); } - if (Semver.gt(version, kibanaVersion)) { + + Object.entries(modelVersionMap).forEach(([version, definition]) => { + assertValidModelVersion(version); + }); + + const { min: minVersion, max: maxVersion } = Object.keys(modelVersionMap).reduce( + (minMax, rawVersion) => { + const version = Number.parseInt(rawVersion, 10); + minMax.min = Math.min(minMax.min, version); + minMax.max = Math.max(minMax.max, version); + return minMax; + }, + { min: Infinity, max: -Infinity } + ); + + if (minVersion > 1) { + throw new Error(`Type ${type.name}: model versioning must start with version 1`); + } + const missingVersions = getMissingVersions( + minVersion, + maxVersion, + Object.keys(modelVersionMap).map((v) => Number.parseInt(v, 10)) + ); + if (missingVersions.length) { throw new Error( - `Invalid migration for type ${type}. Property '${version}' cannot be greater than the current Kibana version '${kibanaVersion}'.` + `Type ${ + type.name + }: gaps between model versions aren't allowed (missing versions: ${missingVersions.join( + ',' + )})` ); } } - function assertValidTransform(fn: any, version: string, type: string) { - if (typeof fn !== 'function') { - throw new Error(`Invalid migration ${type}.${version}: expected a function, but got ${fn}.`); - } + if (type.convertToMultiNamespaceTypeVersion) { + assertValidConvertToMultiNamespaceType( + kibanaVersion, + convertVersion, + type.namespaceType, + type.convertToMultiNamespaceTypeVersion, + type.name + ); } +} - if (migrationsMap) { - assertObject( - migrationsMap, - `Migrations map for type ${name} should be an object like { '2.0.0': (doc) => doc }.` +const assertObjectOrFunction = (entity: any, prefix: string) => { + if (!entity || (typeof entity !== 'function' && typeof entity !== 'object')) { + throw new Error(`${prefix} Got! ${typeof entity}, ${JSON.stringify(entity)}.`); + } +}; + +const assertObject = (obj: any, prefix: string) => { + if (!obj || typeof obj !== 'object') { + throw new Error(`${prefix} Got ${obj}.`); + } +}; + +const assertValidSemver = (kibanaVersion: string, version: string, type: string) => { + if (!Semver.valid(version)) { + throw new Error( + `Invalid migration for type ${type}. Expected all properties to be semvers, but got ${version}.` + ); + } + if (Semver.gt(version, kibanaVersion)) { + throw new Error( + `Invalid migration for type ${type}. Property '${version}' cannot be greater than the current Kibana version '${kibanaVersion}'.` ); + } +}; - Object.entries(migrationsMap).forEach(([version, fn]) => { - assertValidSemver(version, name); - assertValidTransform(fn, version, name); - }); +const assertValidConvertToMultiNamespaceType = ( + kibanaVersion: string, + convertVersion: string | undefined, + namespaceType: SavedObjectsNamespaceType, + convertToMultiNamespaceTypeVersion: string, + type: string +) => { + if (namespaceType !== 'multiple' && namespaceType !== 'multiple-isolated') { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Expected namespaceType to be 'multiple' or 'multiple-isolated', but got '${namespaceType}'.` + ); + } else if (!Semver.valid(convertToMultiNamespaceTypeVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Expected value to be a semver, but got '${convertToMultiNamespaceTypeVersion}'.` + ); + } else if (convertVersion && Semver.neq(convertToMultiNamespaceTypeVersion, convertVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be any other than '${convertVersion}'.` + ); + } else if (Semver.gt(convertToMultiNamespaceTypeVersion, kibanaVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be greater than the current Kibana version '${kibanaVersion}'.` + ); + } else if (Semver.patch(convertToMultiNamespaceTypeVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be used on a patch version (must be like 'x.y.0').` + ); } -} +}; + +const assertValidTransform = (fn: any, version: string, type: string) => { + if (typeof fn !== 'function') { + throw new Error(`Invalid migration ${type}.${version}: expected a function, but got ${fn}.`); + } +}; + +const getMissingVersions = (from: number, to: number, versions: number[]): number[] => { + const versionSet = new Set(versions); + const missing: number[] = []; + for (let i = from; i <= to; i++) { + if (!versionSet.has(i)) { + missing.push(i); + } + } + return missing; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json index 3612fae05aba1..d1751da1050f5 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/tsconfig.json @@ -29,6 +29,7 @@ "@kbn/core-elasticsearch-server-mocks", "@kbn/doc-links", "@kbn/safer-lodash-set", + "@kbn/logging-mocks", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts b/packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts index 5af4866ddd576..5d24b74d3ab89 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SavedObjectSanitizedDoc } from '../serialization'; +import type { SavedObjectUnsanitizedDoc } from '../serialization'; import type { SavedObjectsMigrationLogger } from '../migration'; /** @@ -14,7 +14,7 @@ import type { SavedObjectsMigrationLogger } from '../migration'; * * @public */ -export type SavedObjectModelTransformationDoc = SavedObjectSanitizedDoc; +export type SavedObjectModelTransformationDoc = SavedObjectUnsanitizedDoc; /** * Context passed down to {@link SavedObjectModelTransformationFn | transformation functions}. From 09033c86dbb9c1b417ea540ac9c03b532f509288 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 15 Feb 2023 10:18:44 +0100 Subject: [PATCH 02/68] [Lens] Fix reference line label rendering (#151162) ## Summary Fix #151101 Refactored shared code between reference line and annotation rendering to restore label rendering on Reference line. The root issue was a refactoring of the code who only considered the annotation configuration shape, which slightly differs from the reference lines one, and in particular a stricter check for the label configuration together with the assumption that an icon was always shown in the annotation marker. I've added few new unit tests to detect changes in the future in some annotation logic, but it would be best to extend them in the future. Screenshot 2023-02-14 at 15 29 10 Screenshot 2023-02-14 at 15 29 17 ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../reference_line.test.ts | 5 + .../expression_functions/reference_line.ts | 5 + .../common/types/expression_functions.ts | 4 +- .../reference_lines/reference_line.tsx | 7 +- .../reference_line_annotations.tsx | 15 +-- .../reference_lines/reference_lines.test.tsx | 8 ++ .../components/reference_lines/utils.test.tsx | 125 +++++++++++++++++- .../components/reference_lines/utils.tsx | 25 ++-- .../public/helpers/annotations.tsx | 13 +- 9 files changed, 174 insertions(+), 33 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts index 7e3230d075fcf..6dc19f79bd133 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.test.ts @@ -16,6 +16,7 @@ describe('referenceLine', () => { value: 100, fill: 'above', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -47,6 +48,7 @@ describe('referenceLine', () => { color: '#fff', fill: 'below', textVisibility: true, + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -71,6 +73,7 @@ describe('referenceLine', () => { value: 100, fill: 'none', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -96,6 +99,7 @@ describe('referenceLine', () => { textVisibility: true, fill: 'none', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); @@ -123,6 +127,7 @@ describe('referenceLine', () => { textVisibility, fill: 'none', position: 'bottom', + forAccessor: '', }; const result = referenceLineFunction.fn(null, args, createMockExecutionContext()); diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts index 0f3a3a4f6b1fb..93832291b9835 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/reference_line.ts @@ -105,6 +105,11 @@ export const referenceLineFunction: ReferenceLineFn = { default: FillStyles.NONE, strict: true, }, + forAccessor: { + types: ['string'], + help: '', + default: '', + }, }, fn(table, args) { const textVisibility = diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 3a2d135113779..7d5df1a7394bb 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -329,8 +329,7 @@ export type ExtendedAnnotationLayerConfigResult = ExtendedAnnotationLayerArgs & layerType: typeof LayerTypes.ANNOTATIONS; }; -export interface ReferenceLineArgs - extends Omit { +export interface ReferenceLineArgs extends Omit { name?: string; value: number; fill: FillStyle; @@ -365,6 +364,7 @@ export interface ReferenceLineConfigResult { type: typeof REFERENCE_LINE; layerType: typeof LayerTypes.REFERENCELINE; lineLength: number; + columnToLabel?: string; decorations: [ExtendedReferenceLineDecorationConfig]; } diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx index 6c58171d35ae4..f12362b2c8c21 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_line.tsx @@ -35,6 +35,7 @@ export const ReferenceLine: FC = ({ }) => { const { decorations: [decorationConfig], + columnToLabel, } = layer; if (!decorationConfig) { @@ -42,15 +43,19 @@ export const ReferenceLine: FC = ({ } const { value } = decorationConfig; + const columnToLabelMap: Record = columnToLabel ? JSON.parse(columnToLabel) : {}; const axisGroup = getAxisGroupForReferenceLine(axesConfiguration, decorationConfig, isHorizontal); const formatter = axisGroup?.formatter || xAxisFormatter; const id = `${layer.layerId}-${value}`; + const name = decorationConfig.textVisibility + ? columnToLabelMap[decorationConfig.forAccessor] + : undefined; return ( = ({ paddingMap, isHorizontal, }) => { - const { id, axisGroup, iconPosition, name, textVisibility, value, fill, color } = config; + const { id, axisGroup, iconPosition, name, value, fill, color } = config; const defaultColor = euiLightVars.euiColorDarkShade; // get the position for vertical chart @@ -85,18 +85,9 @@ export const ReferenceLineAnnotations: FC = ({ getOriginalAxisPosition(axisGroup?.position ?? Position.Bottom, isHorizontal) ); // the padding map is built for vertical chart - const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; + const isTextOnlyMarker = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; - const props = getLineAnnotationProps( - config, - { - markerLabel: name, - markerBodyLabel: textVisibility && !hasReducedPadding ? name : undefined, - }, - axesMap, - paddingMap, - isHorizontal - ); + const props = getLineAnnotationProps(config, name, axesMap, isHorizontal, isTextOnlyMarker); const sharedStyle = getSharedStyle(config); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx index 6aa7319c0ba4c..810705a71fa3e 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/reference_lines.test.tsx @@ -536,6 +536,7 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -576,6 +577,7 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -617,12 +619,14 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), createReferenceLine(layerPrefix, 10, { position, lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -667,12 +671,14 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill, value, + forAccessor: '', }), createReferenceLine(layerPrefix, 10, { position: 'bottom', lineStyle: 'solid', fill, value, + forAccessor: '', }), ]} /> @@ -720,12 +726,14 @@ describe('ReferenceLines', () => { lineStyle: 'solid', fill: 'above', value: value1, + forAccessor: '', }), createReferenceLine(layerPrefix, 10, { position, lineStyle: 'solid', fill: 'below', value: value2, + forAccessor: '', }), ]} /> diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx index f46f12d2317ab..ba14980ed5a7b 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.test.tsx @@ -7,8 +7,11 @@ */ import { AxisTypeId, computeInputCombinations, PosType } from './_mocks'; -import { computeChartMargins } from './utils'; -import { AxisConfiguration } from '../../helpers'; +import { computeChartMargins, getLineAnnotationProps } from './utils'; +import { AxesMap, AxisConfiguration, Marker, MarkerBody } from '../../helpers'; +import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; +import { Position } from '@elastic/charts'; +import React from 'react'; describe('reference lines helpers', () => { describe('computeChartMargins', () => { @@ -36,4 +39,122 @@ describe('reference lines helpers', () => { } ); }); + + describe('getLineAnnotationProps', () => { + function getAxesMap({ left, right }: Partial = {}): AxesMap { + return { + left: { groupId: 'yLeft', position: Position.Left, series: [], ...left }, + right: { groupId: 'yRight', position: Position.Right, series: [], ...right }, + }; + } + function getConfig( + customPartialConfig: Partial = {} + ): ReferenceLineAnnotationConfig { + return { + id: 'id', + value: 3, + lineWidth: 5, + textVisibility: false, + iconPosition: 'auto', + axisGroup: getAxesMap().left, + ...customPartialConfig, + }; + } + + it('should render only the line with no marker', () => { + const config = getConfig(); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + it('should render an icon marker', () => { + const config = getConfig({ icon: 'triangle' }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + it('should render only the label', () => { + const config = getConfig({ textVisibility: true }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, true)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + it('should render both icon and text', () => { + const config = getConfig({ icon: 'triangle', textVisibility: true }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'yLeft', + markerPosition: Position.Left, + marker: ( + + ), + markerBody: , + }) + ); + }); + + it('should handle marker rotated label', () => { + const config = getConfig({ + icon: 'triangle', + textVisibility: true, + axisGroup: { groupId: 'x', position: Position.Bottom, series: [] }, + }); + expect(getLineAnnotationProps(config, 'myLabel', getAxesMap(), false, false)).toEqual( + expect.objectContaining({ + groupId: 'x', + markerPosition: Position.Top, + marker: ( + + ), + markerBody: , + }) + ); + }); + }); }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx index 5c56bd8cc6955..33c513a42c603 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/reference_lines/utils.tsx @@ -21,7 +21,6 @@ import { import { FillStyles } from '../../../common/constants'; import { GroupsConfiguration, - LINES_MARKER_SIZE, mapVerticalToHorizontalPlacement, Marker, MarkerBody, @@ -29,7 +28,7 @@ import { getOriginalAxisPosition, AxesMap, } from '../../helpers'; -import { ReferenceLineAnnotationConfig } from './reference_line_annotations'; +import type { ReferenceLineAnnotationConfig } from './reference_line_annotations'; // if there's just one axis, put it on the other one // otherwise use the same axis @@ -84,10 +83,10 @@ export const getSharedStyle = (config: ReferenceLineAnnotationConfig) => ({ export const getLineAnnotationProps = ( config: ReferenceLineAnnotationConfig, - labels: { markerLabel?: string; markerBodyLabel?: string }, + label: string | undefined, axesMap: AxesMap, - paddingMap: Partial>, - isHorizontal: boolean + isHorizontal: boolean, + isTextOnlyMarker: boolean ) => { // get the position for vertical chart const markerPositionVertical = getBaseIconPlacement( @@ -96,27 +95,27 @@ export const getLineAnnotationProps = ( getOriginalAxisPosition(config.axisGroup?.position ?? Position.Bottom, isHorizontal) ); - // the padding map is built for vertical chart - const hasReducedPadding = paddingMap[markerPositionVertical] === LINES_MARKER_SIZE; - const markerPosition = isHorizontal ? mapVerticalToHorizontalPlacement(markerPositionVertical) : markerPositionVertical; + const isMarkerLabelHorizontal = + markerPosition === Position.Bottom || markerPosition === Position.Top; + return { groupId: config.axisGroup?.groupId || 'bottom', marker: ( ), markerBody: ( ), // rotate the position if required diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx index c972b335a1245..93cf570e86bc1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/annotations.tsx @@ -29,7 +29,7 @@ type PartialReferenceLineDecorationConfig = Pick< type PartialMergedAnnotation = Pick< MergedAnnotation, - 'position' | 'icon' | 'textVisibility' | 'label' + 'position' | 'icon' | 'textVisibility' | 'label' | 'isGrouped' >; const isExtendedDecorationConfig = ( @@ -37,6 +37,12 @@ const isExtendedDecorationConfig = ( ): config is PartialReferenceLineDecorationConfig => (config as PartialReferenceLineDecorationConfig)?.iconPosition ? true : false; +export const isAnnotationConfig = ( + config: PartialReferenceLineDecorationConfig | PartialMergedAnnotation +): config is PartialMergedAnnotation => { + return 'isGrouped' in config; +}; + // Note: it does not take into consideration whether the reference line is in view or not export const getLinesCausedPaddings = ( visualConfigs: Array, @@ -52,8 +58,9 @@ export const getLinesCausedPaddings = ( } const { position, icon, textVisibility } = config; const iconPosition = isExtendedDecorationConfig(config) ? config.iconPosition : undefined; + const isLabelVisible = textVisibility && (isAnnotationConfig(config) ? config.label : true); - if (position && (hasIcon(icon) || (textVisibility && 'label' in config))) { + if (position && (hasIcon(icon) || isLabelVisible)) { const placement = getBaseIconPlacement( iconPosition, axesMap, @@ -61,7 +68,7 @@ export const getLinesCausedPaddings = ( ); paddings[placement] = Math.max( paddings[placement] || 0, - LINES_MARKER_SIZE * (textVisibility && 'label' in config && config.label ? 2 : 1) // double the padding size if there's text + LINES_MARKER_SIZE * (isLabelVisible ? 2 : 1) // double the padding size if there's text ); icons[placement] = (icons[placement] || 0) + (hasIcon(icon) ? 1 : 0); } From 4e9d044ec182fc13985e39f3035b9858caa96a5f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 10:54:35 +0100 Subject: [PATCH 03/68] Update dependency react-hook-form to ^7.43.1 (main) (#151145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [react-hook-form](https://www.react-hook-form.com) ([source](https://togithub.com/react-hook-form/react-hook-form)) | [`^7.43.0` -> `^7.43.1`](https://renovatebot.com/diffs/npm/react-hook-form/7.43.0/7.43.1) | [![age](https://badges.renovateapi.com/packages/npm/react-hook-form/7.43.1/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/react-hook-form/7.43.1/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/react-hook-form/7.43.1/compatibility-slim/7.43.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/react-hook-form/7.43.1/confidence-slim/7.43.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
react-hook-form/react-hook-form ### [`v7.43.1`](https://togithub.com/react-hook-form/react-hook-form/releases/tag/v7.43.1): Version 7.43.1 [Compare Source](https://togithub.com/react-hook-form/react-hook-form/compare/v7.43.0...v7.43.1) 🐞 fix [#​9871](https://togithub.com/react-hook-form/react-hook-form/issues/9871) issue with error type ([#​9873](https://togithub.com/react-hook-form/react-hook-form/issues/9873)) 🐞 fix [#​9842](https://togithub.com/react-hook-form/react-hook-form/issues/9842) `clearErrors` method does not support global error ([#​9843](https://togithub.com/react-hook-form/react-hook-form/issues/9843))
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/elastic/kibana). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4349bb747869e..cb0a7b87b2b1b 100644 --- a/package.json +++ b/package.json @@ -862,7 +862,7 @@ "react-fast-compare": "^2.0.4", "react-focus-on": "^3.7.0", "react-grid-layout": "^1.3.4", - "react-hook-form": "^7.43.0", + "react-hook-form": "^7.43.1", "react-intl": "^2.8.0", "react-is": "^17.0.2", "react-markdown": "^6.0.3", diff --git a/yarn.lock b/yarn.lock index 9dc6aaf06c07a..51a9a2144b6e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24088,10 +24088,10 @@ react-grid-layout@^1.3.4: react-draggable "^4.0.0" react-resizable "^3.0.4" -react-hook-form@^7.43.0: - version "7.43.0" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.43.0.tgz#d0c19b5c4ec561fbf8d652869ccb513c11c772e7" - integrity sha512-/rVEz7T0gLdSFwPqutJ1kn2e0sQNyb9ci/hmwEYr2YG0KF/LSuRLvNrf9QWJM+gj88CjDpDW5Bh/1AD7B2+z9Q== +react-hook-form@^7.43.1: + version "7.43.1" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.43.1.tgz#0d0d7822f3f7fc05ffc41d5f012b49b90fcfa0f0" + integrity sha512-+s3+s8LLytRMriwwuSqeLStVjRXFGxgjjx2jED7Z+wz1J/88vpxieRQGvJVvzrzVxshZ0BRuocFERb779m2kNg== react-input-autosize@^3.0.0: version "3.0.0" From b7c0d10d74f38e44397d23a7043d5b32214b8b53 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:23:14 +0100 Subject: [PATCH 04/68] Update cypress (main) (#150967) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [cypress](https://togithub.com/cypress-io/cypress) | [`^12.3.0` -> `^12.5.1`](https://renovatebot.com/diffs/npm/cypress/12.3.0/12.5.1) | [![age](https://badges.renovateapi.com/packages/npm/cypress/12.5.1/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/cypress/12.5.1/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/cypress/12.5.1/compatibility-slim/12.3.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/cypress/12.5.1/confidence-slim/12.3.0)](https://docs.renovatebot.com/merge-confidence/) | | [cypress-axe](https://togithub.com/component-driven/cypress-axe) | [`^1.2.0` -> `^1.3.0`](https://renovatebot.com/diffs/npm/cypress-axe/1.2.0/1.3.0) | [![age](https://badges.renovateapi.com/packages/npm/cypress-axe/1.3.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/cypress-axe/1.3.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/cypress-axe/1.3.0/compatibility-slim/1.2.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/cypress-axe/1.3.0/confidence-slim/1.2.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
cypress-io/cypress ### [`v12.5.1`](https://togithub.com/cypress-io/cypress/releases/tag/v12.5.1) [Compare Source](https://togithub.com/cypress-io/cypress/compare/v12.5.0...v12.5.1) Changelog: https://docs.cypress.io/guides/references/changelog#​12-5-1 ### [`v12.5.0`](https://togithub.com/cypress-io/cypress/releases/tag/v12.5.0) [Compare Source](https://togithub.com/cypress-io/cypress/compare/v12.4.1...v12.5.0) Changelog: https://docs.cypress.io/guides/references/changelog#​12-5-0 ### [`v12.4.1`](https://togithub.com/cypress-io/cypress/releases/tag/v12.4.1) [Compare Source](https://togithub.com/cypress-io/cypress/compare/v12.4.0...v12.4.1) Changelog: https://docs.cypress.io/guides/references/changelog#​12-4-1 ### [`v12.4.0`](https://togithub.com/cypress-io/cypress/releases/tag/v12.4.0) [Compare Source](https://togithub.com/cypress-io/cypress/compare/v12.3.0...v12.4.0) Changelog: https://docs.cypress.io/guides/references/changelog#​12-4-0
component-driven/cypress-axe ### [`v1.3.0`](https://togithub.com/component-driven/cypress-axe/releases/tag/v1.3.0) [Compare Source](https://togithub.com/component-driven/cypress-axe/compare/v1.2.0...v1.3.0) ##### Features - Allow to use custom axe-core location ([#​150](https://togithub.com/component-driven/cypress-axe/issues/150)) ([91c72d2](https://togithub.com/component-driven/cypress-axe/commit/91c72d2573cedec135789bc0b7cef80047f5abf9)), closes [#​146](https://togithub.com/component-driven/cypress-axe/issues/146) [#​134](https://togithub.com/component-driven/cypress-axe/issues/134) [#​84](https://togithub.com/component-driven/cypress-axe/issues/84) [#​75](https://togithub.com/component-driven/cypress-axe/issues/75)
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/elastic/kibana). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Patryk Kopyciński --- package.json | 4 ++-- yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index cb0a7b87b2b1b..bbd67fbd89633 100644 --- a/package.json +++ b/package.json @@ -1321,8 +1321,8 @@ "cssnano": "^5.1.12", "cssnano-preset-default": "^5.2.12", "csstype": "^3.0.2", - "cypress": "^12.3.0", - "cypress-axe": "^1.2.0", + "cypress": "^12.5.1", + "cypress-axe": "^1.3.0", "cypress-file-upload": "^5.0.8", "cypress-multi-reporters": "^1.6.2", "cypress-pipe": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index 51a9a2144b6e9..99e9088e676e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12912,10 +12912,10 @@ cyclist@~0.2.2: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= -cypress-axe@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.2.0.tgz#224936a3346055f6c22636e645746cc903aaa947" - integrity sha512-mK7rxc8+e0IM519efgkzVBQRYLapy55TOVNbWZsbX3495xFzf8NnkMddUaHAGgAnF+kcyJU7zNPu3lTiOD58WQ== +cypress-axe@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.3.0.tgz#255ef8ef8e88747f2a72ceb7f7c60e8185b7852b" + integrity sha512-b2zAva1+uRwGA7r/JzP7C/64YHu9Fa8RsHRIrapUDzJeGLEQImz86FbwRW/lBamrEt7YHzGRwuJizXKTyQBsfQ== cypress-file-upload@^5.0.8: version "5.0.8" @@ -12954,10 +12954,10 @@ cypress-recurse@^1.27.0: dependencies: humanize-duration "^3.27.3" -cypress@^12.3.0: - version "12.3.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.3.0.tgz#ae3fb0540aef4b5eab1ef2bcd0760caf2992b8bf" - integrity sha512-ZQNebibi6NBt51TRxRMYKeFvIiQZ01t50HSy7z/JMgRVqBUey3cdjog5MYEbzG6Ktti5ckDt1tfcC47lmFwXkw== +cypress@^12.5.1: + version "12.5.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.5.1.tgz#effdcccdd5a6187d61d497300903d4f3b5b21b6e" + integrity sha512-ZmCmJ3lsyeOpBfh410m5+AO2CO1AxAzFBt7k6/uVbNcrNZje1vdiwYTpj2ksPKg9mjr9lR6V8tmlDNMvr4H/YQ== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" From 560c3b96f81ce2167a3d104bf97e4fe5a1e7fb76 Mon Sep 17 00:00:00 2001 From: Francesco Gualazzi Date: Wed, 15 Feb 2023 11:50:29 +0100 Subject: [PATCH 05/68] profiling: fix setup page and instructions (#150885) Resolves https://github.com/elastic/prodfiler/issues/2961. We update the setup information text instructions to add data for Universal Profiling Beta. Signed-off-by: inge4pres Co-authored-by: boriskirov Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/check_setup.tsx | 70 +++++++++++++++++-- .../public/components/no_data_page.tsx | 11 +-- .../profiling_header_action_menu.tsx | 2 +- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/profiling/public/components/check_setup.tsx b/x-pack/plugins/profiling/public/components/check_setup.tsx index 566ff7382aacf..aff650446303f 100644 --- a/x-pack/plugins/profiling/public/components/check_setup.tsx +++ b/x-pack/plugins/profiling/public/components/check_setup.tsx @@ -4,7 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiText } from '@elastic/eui'; +import { + EuiButton, + EuiCallOut, + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiLoadingSpinner, + EuiText, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { AsyncStatus, useAsync } from '../hooks/use_async'; @@ -49,6 +58,13 @@ export function CheckSetup({ children }: { children: React.ReactElement }) { + + + {i18n.translate('xpack.profiling.noDataConfig.loading.loaderText', { + defaultMessage: 'Loading data sources', + })} + + ); @@ -81,12 +97,54 @@ export function CheckSetup({ children }: { children: React.ReactElement }) { action: { elasticAgent: { description: ( - - {i18n.translate('xpack.profiling.noDataConfig.action.title', { - defaultMessage: `Universal Profiling provides fleet-wide, whole-system, continuous profiling with zero instrumentation. + + + {i18n.translate('xpack.profiling.noDataConfig.action.title', { + defaultMessage: `Universal Profiling provides fleet-wide, whole-system, continuous profiling with zero instrumentation. Understand what lines of code are consuming compute resources, at all times, and across your entire infrastructure.`, - })} - + })} + + + +
    +
  • + {i18n.translate('xpack.profiling.noDataConfig.action.dataRetention', { + defaultMessage: `Normal data storage costs apply for profiling data stored in Elasticsearch. + To control data retention. `, + })} + + {i18n.translate('xpack.profiling.noDataConfig.readMore.linkLabel', { + defaultMessage: 'Read more', + })} + +
  • +
  • + {i18n.translate('xpack.profiling.noDataConfig.action.legalBetaTerms', { + defaultMessage: `By using this feature, you acknowledge that you have read and agree to `, + })} + + {i18n.translate('xpack.profiling.noDataConfig.betaTerms.linkLabel', { + defaultMessage: 'Elastic Beta Release Terms', + })} + +
  • +
+
+ + ), onClick: (event: React.MouseEvent) => { event.preventDefault(); diff --git a/x-pack/plugins/profiling/public/components/no_data_page.tsx b/x-pack/plugins/profiling/public/components/no_data_page.tsx index d045b4146dd9c..52e5e35abe9d3 100644 --- a/x-pack/plugins/profiling/public/components/no_data_page.tsx +++ b/x-pack/plugins/profiling/public/components/no_data_page.tsx @@ -124,7 +124,8 @@ docker.elastic.co/observability/profiling-agent:stable /root/pf-host-agent \\ }), content: ( - wget -O- https://releases.prodfiler.com/stable/pf-host-agent_linux_amd64.tgz | tar xz + wget -O pf-host-agent.tgz "https://ela.st/pf-host-agent-amd64" && tar xzf + pf-host-agent.tgz ), }, @@ -162,8 +163,8 @@ docker.elastic.co/observability/profiling-agent:stable /root/pf-host-agent \\ 'Open the URL below and download the right DEB package for your CPU architecture:', }), content: ( - - https://releases.prodfiler.com/stable/index.html + + https://ela.st/pf-host-agent-linux ), }, @@ -212,8 +213,8 @@ docker.elastic.co/observability/profiling-agent:stable /root/pf-host-agent \\ 'Open the URL below and download the right RPM package for your CPU architecture:', }), content: ( - - https://releases.prodfiler.com/stable/index.html + + https://ela.st/pf-host-agent-linux ), }, diff --git a/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx b/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx index 86d2ce4b28b1a..21aba30123c55 100644 --- a/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx +++ b/x-pack/plugins/profiling/public/components/profiling_header_action_menu.tsx @@ -16,7 +16,7 @@ export function ProfilingHeaderActionMenu() { - + {i18n.translate('xpack.profiling.headerActionMenu.addData', { From 45bbed223c2bdd7724e74c6d2b656c058b5c3482 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 15 Feb 2023 12:09:15 +0100 Subject: [PATCH 06/68] [Lens] Fix absolute time shift validation when using relative time range in dashboard (#150984) ## Summary Fix #150313 as noted in https://github.com/elastic/kibana/pull/150418#issuecomment-1424925427 I could not find any particular way to test it visually other than with a unit test. ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Stratoula Kalafateli --- .../common/search/aggs/utils/parse_time_shift.test.ts | 9 +++++++++ .../data/common/search/aggs/utils/parse_time_shift.ts | 8 +++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/plugins/data/common/search/aggs/utils/parse_time_shift.test.ts b/src/plugins/data/common/search/aggs/utils/parse_time_shift.test.ts index 03c1b901580c8..f8df9ded94ec0 100644 --- a/src/plugins/data/common/search/aggs/utils/parse_time_shift.test.ts +++ b/src/plugins/data/common/search/aggs/utils/parse_time_shift.test.ts @@ -119,6 +119,15 @@ describe('parse time shifts', () => { } } }); + + it('should validate against absolute time range values', () => { + expect( + validateAbsoluteTimeShift(`startAt(${futureDateString})`, { + from: 'now-1y', + to: 'now', + }) + ).toBe(REASON_IDS.shiftAfterTimeRange); + }); }); describe('parseAbsoluteTimeShift', () => { diff --git a/src/plugins/data/common/search/aggs/utils/parse_time_shift.ts b/src/plugins/data/common/search/aggs/utils/parse_time_shift.ts index eb0241865eb3e..4b0173e75c192 100644 --- a/src/plugins/data/common/search/aggs/utils/parse_time_shift.ts +++ b/src/plugins/data/common/search/aggs/utils/parse_time_shift.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ import moment from 'moment'; +import { getAbsoluteTimeRange } from '../../../query/timefilter/get_time'; import { TimeRange } from '../../../types'; const ALLOWED_UNITS = ['s', 'm', 'h', 'd', 'w', 'M', 'y'] as const; @@ -135,10 +136,11 @@ export function validateAbsoluteTimeShift( return REASON_IDS.invalidDate; } if (timeRange) { - const duration = moment(timeRange.to).diff(moment(timeRange.from)); + const absTimeRange = getAbsoluteTimeRange(timeRange); + const duration = moment(absTimeRange.to).diff(moment(absTimeRange.from)); if ( - (anchor === START_AT_ANCHOR && tsMoment.isAfter(timeRange.from)) || - tsMoment.subtract(duration).isAfter(timeRange.from) + (anchor === START_AT_ANCHOR && tsMoment.isAfter(absTimeRange.from)) || + tsMoment.subtract(duration).isAfter(absTimeRange.from) ) return REASON_IDS.shiftAfterTimeRange; } From f443109eeae6af9873e39f16b53825941690c3be Mon Sep 17 00:00:00 2001 From: Sergi Massaneda Date: Wed, 15 Feb 2023 12:27:33 +0100 Subject: [PATCH 07/68] [Security Solution] Cell Actions and Security triggers refactor (#150213) ## Summary issue https://github.com/elastic/kibana/issues/145657 General actions refactor to complete the CellActions package architecture. These changes won't cause any noticeable change in the application behavior. They include: *kbn-cell-actions* - Generic actions (filterIn/Out & copyToClipboard) migrated to the package. - `factory` implemented to export actions from the package. - `CellAction` types generics. *SecuritySolution* - `SecurityCellAction` type encapsulated in new _actions/types.ts_ file. - Actions changes to use new generic actions and also to be exported using the package `factory`. - Actions registration simplified to more declarative. - Triggers and types moved to owned constants.ts enums. - FilterIn/Out default actions unified timeline in a single implementation using `metadata.scopeId`. - Timeline Trigger removed, no longer needed. - `SecurityCellActions` component to get the proper metadata typings and code suggestions inside Security. - Security CellActions metadata TSDoc expanded ### Factory The action `factory` is necessary because we need a different action `id`s for every action in the _uiActions_ registry, meaning that if different triggers/solutions need different `order`, `type`, `isCompatible` function, or any customization, they will need to register an action with a different id, before attaching it. The `CellActionFactory` implementation simplifies this workflow by allowing action creation (only `id` param is required but any other action property can be assigned) and also allows to `combine` factories, returning a new factory with some properties overridden. This is useful for extending generic actions into more specific implementations. ### SecurityCellActions They are Security typed generic `CellActions` components, just for convenience when using CellActions, they show the proper TSDoc information about `metadata` contents, `triggerId`, and so on. It was not possible when using the package exports directly. ![scopeId TSDoc](https://user-images.githubusercontent.com/17747913/218807646-c4166d58-e1f4-41c6-ad2d-3ee801cf037c.png) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../src/__stories__/cell_actions.stories.tsx | 3 +- .../copy_to_clipboard.test.ts | 20 +-- .../copy_to_clipboard/copy_to_clipboard.ts | 55 +++++++ .../src/actions/copy_to_clipboard/index.ts | 9 ++ .../src/actions/factory.test.ts | 146 +++++++++++++++++ .../kbn-cell-actions/src/actions/factory.ts | 76 +++++++++ .../src/actions/filter/create_filter.test.ts | 114 ++++++++++++++ .../src/actions/filter/create_filter.ts | 57 +++++++ .../src/actions/filter/filter_in.test.ts | 122 +++++++++++++++ .../src/actions/filter/filter_in.ts | 49 ++++++ .../src/actions/filter/filter_out.test.ts | 116 ++++++++++++++ .../src/actions/filter/filter_out.ts | 53 +++++++ .../src/actions/filter/index.ts | 10 ++ .../src/components/cell_actions.test.tsx | 2 +- .../src/components/cell_actions.tsx | 9 +- .../components/hover_actions_popover.test.tsx | 12 +- .../src/components/hover_actions_popover.tsx | 6 +- .../src/components/inline_actions.test.tsx | 4 +- .../src/components/inline_actions.tsx | 6 +- packages/kbn-cell-actions/src/constants.ts | 15 ++ packages/kbn-cell-actions/src/hooks/index.ts | 1 + .../use_data_grid_column_cell_actions.tsx | 16 +- .../src/hooks/use_load_actions.test.ts | 10 +- .../src/hooks/use_load_actions.ts | 16 +- packages/kbn-cell-actions/src/index.ts | 30 +++- .../kbn-cell-actions/src/mocks/helpers.ts | 1 + packages/kbn-cell-actions/src/types.ts | 47 ++++-- packages/kbn-cell-actions/tsconfig.json | 3 + .../security_solution/common/constants.ts | 4 - .../cypress/screens/alerts.ts | 10 +- .../cypress/screens/network/flows.ts | 11 +- .../cypress/screens/timeline.ts | 9 +- .../add_to_timeline.test.ts | 32 ++-- .../cell_action/add_to_timeline.ts | 71 +++++++++ .../default/add_to_timeline.tsx | 77 --------- .../public/actions/add_to_timeline/index.ts | 4 +- .../lens/add_to_timeline.test.ts | 6 +- .../add_to_timeline/lens/add_to_timeline.ts | 2 +- .../public/actions/constants.ts | 18 +++ .../cell_action/copy_to_clipboard.ts | 27 ++++ .../default/copy_to_clipboard.tsx | 47 ------ .../public/actions/copy_to_clipboard/index.ts | 4 +- .../lens/copy_to_clipboard.test.ts | 6 +- .../lens/copy_to_clipboard.ts | 2 +- .../filter/cell_action/filter_in.test.ts | 134 ++++++++++++++++ .../actions/filter/cell_action/filter_in.ts | 57 +++++++ .../filter/cell_action/filter_out.test.ts | 104 ++++++++++++ .../actions/filter/cell_action/filter_out.ts | 57 +++++++ .../actions/filter/default/filter_in.test.ts | 74 --------- .../actions/filter/default/filter_in.tsx | 42 ----- .../actions/filter/default/filter_out.test.ts | 74 --------- .../actions/filter/default/filter_out.tsx | 42 ----- .../public/actions/filter/helpers.ts | 50 ------ .../public/actions/filter/index.ts | 7 +- .../actions/filter/timeline/filter_in.test.ts | 83 ---------- .../actions/filter/timeline/filter_in.tsx | 60 ------- .../filter/timeline/filter_out.test.ts | 83 ---------- .../actions/filter/timeline/filter_out.tsx | 60 ------- .../public/actions/register.ts | 148 ++++++------------ .../show_top_n.test.tsx | 31 +--- .../{default => cell_action}/show_top_n.tsx | 61 +++----- .../public/actions/show_top_n/index.ts | 2 +- .../show_top_n/show_top_n_component.test.tsx | 1 + .../show_top_n/show_top_n_component.tsx | 4 +- .../toggle_column.test.ts | 15 +- .../cell_action/toggle_column.ts | 84 ++++++++++ .../toggle_column/default/toggle_column.ts | 104 ------------ .../public/actions/toggle_column/index.tsx | 2 +- .../security_solution/public/actions/types.ts | 45 ++++++ .../security_solution/public/actions/utils.ts | 3 +- .../common/components/cell_actions/index.ts | 36 +++++ .../components/data_table/index.test.tsx | 4 +- .../common/components/data_table/index.tsx | 16 +- .../components/event_details/columns.tsx | 7 +- .../cti_details/enrichment_summary.tsx | 11 +- .../event_details/overview/overview_card.tsx | 11 +- .../table/summary_value_cell.tsx | 11 +- .../ml/__snapshots__/entity.test.tsx.snap | 2 +- .../public/common/components/ml/entity.tsx | 9 +- .../score/__snapshots__/score.test.tsx.snap | 4 +- .../common/components/ml/score/score.tsx | 13 +- .../common/components/tables/helpers.tsx | 9 +- .../alerts_by_type_panel/columns.tsx | 13 +- .../host_risk_score_table/columns.tsx | 13 +- .../hosts/components/hosts_table/columns.tsx | 25 +-- .../components/network_dns_table/columns.tsx | 14 +- .../network_top_countries_table/columns.tsx | 13 +- .../network_top_n_flow_table/columns.tsx | 19 ++- .../explore/network/pages/details/index.tsx | 13 +- .../user_risk_score_table/columns.tsx | 13 +- .../entity_analytics/risk_score/columns.tsx | 27 ++-- .../field_renderers/field_renderers.tsx | 21 +-- 92 files changed, 1863 insertions(+), 1206 deletions(-) rename {x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default => packages/kbn-cell-actions/src/actions/copy_to_clipboard}/copy_to_clipboard.test.ts (63%) create mode 100644 packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts create mode 100644 packages/kbn-cell-actions/src/actions/copy_to_clipboard/index.ts create mode 100644 packages/kbn-cell-actions/src/actions/factory.test.ts create mode 100644 packages/kbn-cell-actions/src/actions/factory.ts create mode 100644 packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts create mode 100644 packages/kbn-cell-actions/src/actions/filter/create_filter.ts create mode 100644 packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts create mode 100644 packages/kbn-cell-actions/src/actions/filter/filter_in.ts create mode 100644 packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts create mode 100644 packages/kbn-cell-actions/src/actions/filter/filter_out.ts create mode 100644 packages/kbn-cell-actions/src/actions/filter/index.ts create mode 100644 packages/kbn-cell-actions/src/constants.ts rename x-pack/plugins/security_solution/public/actions/add_to_timeline/{default => cell_action}/add_to_timeline.test.ts (80%) create mode 100644 x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/add_to_timeline/default/add_to_timeline.tsx create mode 100644 x-pack/plugins/security_solution/public/actions/constants.ts create mode 100644 x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default/copy_to_clipboard.tsx create mode 100644 x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts create mode 100644 x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts create mode 100644 x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts create mode 100644 x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/default/filter_in.test.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/default/filter_in.tsx delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/default/filter_out.test.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/default/filter_out.tsx delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/helpers.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.test.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.tsx delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.test.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.tsx rename x-pack/plugins/security_solution/public/actions/show_top_n/{default => cell_action}/show_top_n.test.tsx (77%) rename x-pack/plugins/security_solution/public/actions/show_top_n/{default => cell_action}/show_top_n.tsx (73%) rename x-pack/plugins/security_solution/public/actions/toggle_column/{default => cell_action}/toggle_column.test.ts (84%) create mode 100644 x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts delete mode 100644 x-pack/plugins/security_solution/public/actions/toggle_column/default/toggle_column.ts create mode 100644 x-pack/plugins/security_solution/public/actions/types.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/cell_actions/index.ts diff --git a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx index 4c0f362d3ddf1..657dcc93416b8 100644 --- a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx +++ b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx @@ -11,7 +11,8 @@ import { ComponentStory } from '@storybook/react'; import { CellActionsProvider } from '../context/cell_actions_context'; import { makeAction } from '../mocks/helpers'; import { CellActions } from '../components/cell_actions'; -import { CellActionsMode, type CellActionsProps } from '../types'; +import { CellActionsMode } from '../constants'; +import type { CellActionsProps } from '../types'; const TRIGGER_ID = 'testTriggerId'; diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default/copy_to_clipboard.test.ts b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.test.ts similarity index 63% rename from x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default/copy_to_clipboard.test.ts rename to packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.test.ts index 6c17b4019f98b..395fbd13cfadf 100644 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default/copy_to_clipboard.test.ts +++ b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.test.ts @@ -1,23 +1,25 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -import { KibanaServices } from '../../../common/lib/kibana'; -import { createCopyToClipboardAction } from './copy_to_clipboard'; -import type { CellActionExecutionContext } from '@kbn/cell-actions'; +import { createCopyToClipboardActionFactory } from './copy_to_clipboard'; +import type { CellActionExecutionContext } from '../../types'; +import type { NotificationsStart } from '@kbn/core/public'; -jest.mock('../../../common/lib/kibana'); const mockSuccessToast = jest.fn(); -KibanaServices.get().notifications.toasts.addSuccess = mockSuccessToast; const mockCopy = jest.fn((text: string) => true); jest.mock('copy-to-clipboard', () => (text: string) => mockCopy(text)); -describe('Default createCopyToClipboardAction', () => { - const copyToClipboardAction = createCopyToClipboardAction({ order: 1 }); +describe('Default createCopyToClipboardActionFactory', () => { + const copyToClipboardActionFactory = createCopyToClipboardActionFactory({ + notifications: { toasts: { addSuccess: mockSuccessToast } } as unknown as NotificationsStart, + }); + const copyToClipboardAction = copyToClipboardActionFactory({ id: 'testAction' }); const context = { field: { name: 'user.name', value: 'the value', type: 'text' }, } as CellActionExecutionContext; diff --git a/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts new file mode 100644 index 0000000000000..9d4860da1c77c --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import copy from 'copy-to-clipboard'; +import { i18n } from '@kbn/i18n'; +import type { NotificationsStart } from '@kbn/core/public'; +import { COPY_CELL_ACTION_TYPE } from '../../constants'; +import { createCellActionFactory } from '../factory'; + +const ICON = 'copyClipboard'; +const COPY_TO_CLIPBOARD = i18n.translate('cellActions.actions.copyToClipboard.displayName', { + defaultMessage: 'Copy to Clipboard', +}); +const COPY_TO_CLIPBOARD_SUCCESS = i18n.translate( + 'cellActions.actions.copyToClipboard.successMessage', + { + defaultMessage: 'Copied to the clipboard', + } +); + +export const createCopyToClipboardActionFactory = createCellActionFactory( + ({ notifications }: { notifications: NotificationsStart }) => ({ + type: COPY_CELL_ACTION_TYPE, + getIconType: () => ICON, + getDisplayName: () => COPY_TO_CLIPBOARD, + getDisplayNameTooltip: () => COPY_TO_CLIPBOARD, + isCompatible: async ({ field }) => field.name != null, + execute: async ({ field }) => { + let textValue: undefined | string; + if (field.value != null) { + textValue = Array.isArray(field.value) + ? field.value.map((value) => `"${value}"`).join(', ') + : `"${field.value}"`; + } + const text = textValue ? `${field.name}: ${textValue}` : field.name; + const isSuccess = copy(text, { debug: true }); + + if (isSuccess) { + notifications.toasts.addSuccess( + { + title: COPY_TO_CLIPBOARD_SUCCESS, + }, + { + toastLifeTimeMs: 800, + } + ); + } + }, + }) +); diff --git a/packages/kbn-cell-actions/src/actions/copy_to_clipboard/index.ts b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/index.ts new file mode 100644 index 0000000000000..a827edbfcad59 --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { createCopyToClipboardActionFactory } from './copy_to_clipboard'; diff --git a/packages/kbn-cell-actions/src/actions/factory.test.ts b/packages/kbn-cell-actions/src/actions/factory.test.ts new file mode 100644 index 0000000000000..978f2a342dff2 --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/factory.test.ts @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { makeActionContext } from '../mocks/helpers'; +import { createCellActionFactory } from './factory'; + +const mockContext = makeActionContext(); + +const mockActionIsCompatible = jest.fn(async () => true); +const mockActionExecute = jest.fn(async () => {}); + +const testAction = { + id: 'genericTestId', + type: 'genericCellActionType', + getIconType: () => 'test-icon', + getDisplayName: () => 'test name', + isCompatible: mockActionIsCompatible, + execute: mockActionExecute, +}; +const createTestAction = (type: string) => ({ + ...testAction, + type, +}); + +describe('createCellActionFactory', () => { + const id = 'testId'; + const type = 'testActionType'; + + const createActionFactory = createCellActionFactory(createTestAction); + const actionFactory = createActionFactory(type); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should create action factory executes the action creation', () => { + expect(actionFactory({ id: 'test' }).type).toBe(type); + }); + + it('should create action with id only', () => { + const action = actionFactory({ id }); + expect(action.id).toEqual(id); + expect(action.order).toBeUndefined(); + expect(action.getIconType).toBe(testAction.getIconType); + expect(action.getDisplayName).toBe(testAction.getDisplayName); + expect(action.isCompatible).toBe(testAction.isCompatible); + expect(action.execute).toBe(testAction.execute); + }); + + it('should create action with order', () => { + const order = 1234; + const action = actionFactory({ id, order }); + expect(action.order).toEqual(order); + }); + + it('should create action with custom execute and isCompatible', async () => { + const customExecute = jest.fn(); + const customIsCompatible = jest.fn(); + const action = actionFactory({ + id, + execute: customExecute, + isCompatible: customIsCompatible, + }); + + await action.isCompatible(mockContext); + expect(customIsCompatible).toHaveBeenCalledWith(mockContext); + expect(mockActionIsCompatible).not.toHaveBeenCalled(); + + await action.execute(mockContext); + expect(customExecute).toHaveBeenCalledWith(mockContext); + expect(mockActionExecute).not.toHaveBeenCalled(); + }); + + it('should create action with custom execute', async () => { + const customExecute = jest.fn(); + const action = actionFactory({ id, execute: customExecute }); + + await action.isCompatible(mockContext); + expect(mockActionIsCompatible).toHaveBeenCalledWith(mockContext); + + await action.execute(mockContext); + expect(customExecute).toHaveBeenCalledWith(mockContext); + expect(mockActionExecute).not.toHaveBeenCalled(); + }); + + it('should create action with custom isCompatible', async () => { + const customIsCompatible = jest.fn(async () => true); + const action = actionFactory({ id, isCompatible: customIsCompatible }); + + await action.isCompatible(mockContext); + expect(mockActionIsCompatible).toHaveBeenCalledWith(mockContext); + expect(customIsCompatible).toHaveBeenCalledWith(mockContext); + + await action.execute(mockContext); + expect(mockActionExecute).toHaveBeenCalledWith(mockContext); + }); + + describe('combine', () => { + it('should combine factory with new icon', () => { + const newType = 'action-type-2'; + const combinedFactory = actionFactory.combine({ + type: newType, + }); + const action = combinedFactory({ id }); + expect(action.type).toEqual(newType); + }); + + it('should combine factory with isCompatible function', async () => { + const combinedIsCompatible = jest.fn(async () => true); + const customIsCompatible = jest.fn(async () => true); + const combinedFactory = actionFactory.combine({ isCompatible: combinedIsCompatible }); + const action = combinedFactory({ id, isCompatible: customIsCompatible }); + + await action.isCompatible(mockContext); + expect(mockActionIsCompatible).toHaveBeenCalledWith(mockContext); + expect(combinedIsCompatible).toHaveBeenCalledWith(mockContext); + expect(customIsCompatible).toHaveBeenCalledWith(mockContext); + + await action.execute(mockContext); + expect(mockActionExecute).toHaveBeenCalledWith(mockContext); + }); + + it('should combine factory with custom execute and isCompatible', async () => { + const combinedExecute = jest.fn(); + const combinedIsCompatible = jest.fn(); + const combinedFactory = actionFactory.combine({ + execute: combinedExecute, + isCompatible: combinedIsCompatible, + }); + const action = combinedFactory({ id }); + + await action.isCompatible(mockContext); + expect(combinedIsCompatible).toHaveBeenCalledWith(mockContext); + expect(mockActionIsCompatible).not.toHaveBeenCalled(); + + await action.execute(mockContext); + expect(combinedExecute).toHaveBeenCalledWith(mockContext); + expect(mockActionExecute).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/kbn-cell-actions/src/actions/factory.ts b/packages/kbn-cell-actions/src/actions/factory.ts new file mode 100644 index 0000000000000..d2f2da416425a --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/factory.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { CellAction, CellActionExtend, CellActionFactory, CellActionTemplate } from '../types'; + +export const createCellActionFactory = ( + actionCreator: (params: P) => CellActionTemplate +) => { + return (params: P): CellActionFactory => { + const action = actionCreator(params); + return createFactory(action); + }; +}; + +const createFactory = ( + actionTemplate: CellActionTemplate +): CellActionFactory => { + const factory = (extend: CellActionExtend): A => + extendAction(actionTemplate, extend); + + factory.combine = ( + partialActionTemplate: Partial> + ): CellActionFactory => { + const combinedActions = extendActionTemplate(actionTemplate, partialActionTemplate); + return createFactory(combinedActions); + }; + + return factory; +}; + +// extends the template to create another template +const extendActionTemplate = ( + action: CellActionTemplate, + extend: Partial> +): CellActionTemplate => _extendAction>(action, extend); + +// extends the template to create a full action (with id) +const extendAction = ( + action: CellActionTemplate, + extend: CellActionExtend +): C => _extendAction(action, extend); + +const _extendAction = >( + actionTemplate: CellActionTemplate, + extend: Partial +): C => { + const { isCompatible: extendedIsCompatible, execute: extendedExecute, ...rest } = extend; + + let isCompatible = actionTemplate.isCompatible; + if (extendedIsCompatible) { + if (extendedExecute) { + isCompatible = extendedIsCompatible; + } else { + isCompatible = async (context) => { + // call extended and default `isCompatible` to make sure the default `execute` will run properly + return ( + (await actionTemplate.isCompatible(context)) && (await extendedIsCompatible(context)) + ); + }; + } + } + + const execute = extendedExecute ?? actionTemplate.execute; + + return { + ...(actionTemplate as C), + isCompatible, + execute, + ...rest, + }; +}; diff --git a/packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts b/packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts new file mode 100644 index 0000000000000..12ae2fa16a2dd --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createFilter } from './create_filter'; + +const field = 'field.name'; +const value = 'the-value'; + +describe('createFilter', () => { + it.each([ + { caseName: 'string', caseValue: value }, + { caseName: 'array', caseValue: [value] }, + ])('should return filter with $caseName value', ({ caseValue }) => { + expect(createFilter({ key: field, value: caseValue, negate: false })).toEqual({ + meta: { + alias: null, + disabled: false, + type: 'phrase', + key: field, + negate: false, + value, + params: { + query: value, + }, + }, + query: { + match: { + [field]: { + query: value, + type: 'phrase', + }, + }, + }, + }); + }); + + it.each([ + { caseName: 'string', caseValue: value }, + { caseName: 'array', caseValue: [value] }, + ])('should return negate filter with $caseName value', ({ caseValue }) => { + expect(createFilter({ key: field, value: caseValue, negate: true })).toEqual({ + meta: { + alias: null, + disabled: false, + type: 'phrase', + key: field, + negate: true, + value, + params: { + query: value, + }, + }, + query: { + match: { + [field]: { + query: value, + type: 'phrase', + }, + }, + }, + }); + }); + + it.each([ + { caseName: 'null', caseValue: null }, + { caseName: 'undefined', caseValue: undefined }, + { caseName: 'empty string', caseValue: '' }, + { caseName: 'empty array', caseValue: [] }, + ])('should return exist filter with $caseName value', ({ caseValue }) => { + expect(createFilter({ key: field, value: caseValue, negate: false })).toEqual({ + query: { + exists: { + field, + }, + }, + meta: { + alias: null, + disabled: false, + key: field, + negate: false, + type: 'exists', + value: 'exists', + }, + }); + }); + + it.each([ + { caseName: 'null', caseValue: null }, + { caseName: 'undefined', caseValue: undefined }, + { caseName: 'empty string', caseValue: '' }, + { caseName: 'empty array', caseValue: [] }, + ])('should return negate exist filter with $caseName value', ({ caseValue }) => { + expect(createFilter({ key: field, value: caseValue, negate: true })).toEqual({ + query: { + exists: { + field, + }, + }, + meta: { + alias: null, + disabled: false, + key: field, + negate: true, + type: 'exists', + value: 'exists', + }, + }); + }); +}); diff --git a/packages/kbn-cell-actions/src/actions/filter/create_filter.ts b/packages/kbn-cell-actions/src/actions/filter/create_filter.ts new file mode 100644 index 0000000000000..1dcb44e2b73e7 --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/filter/create_filter.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { Filter } from '@kbn/es-query'; + +export const isEmptyFilterValue = (value: string[] | string | null | undefined) => + value == null || value.length === 0; + +export const createFilter = ({ + key, + value, + negate, +}: { + key: string; + value: string[] | string | null | undefined; + negate: boolean; +}): Filter => { + const queryValue = !isEmptyFilterValue(value) ? (Array.isArray(value) ? value[0] : value) : null; + const meta = { alias: null, disabled: false, key, negate }; + + if (queryValue == null) { + return { + query: { + exists: { + field: key, + }, + }, + meta: { + ...meta, + type: 'exists', + value: 'exists', + }, + }; + } + return { + meta: { + ...meta, + type: 'phrase', + value: queryValue, + params: { + query: queryValue, + }, + }, + query: { + match: { + [key]: { + query: queryValue, + type: 'phrase', + }, + }, + }, + }; +}; diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts b/packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts new file mode 100644 index 0000000000000..f75447fe7458b --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { FilterManager } from '@kbn/data-plugin/public'; +import { createFilterInActionFactory } from './filter_in'; +import { makeActionContext } from '../../mocks/helpers'; + +const mockFilterManager = { addFilters: jest.fn() } as unknown as FilterManager; + +const mockCreateFilter = jest.fn((_: any) => ({})); +jest.mock('./create_filter', () => ({ + ...jest.requireActual('./create_filter'), + createFilter: (params: any) => mockCreateFilter(params), +})); + +const fieldName = 'user.name'; +const value = 'the value'; + +describe('createFilterInActionFactory', () => { + const filterInActionFactory = createFilterInActionFactory({ + filterManager: mockFilterManager, + }); + const filterInAction = filterInActionFactory({ id: 'testAction' }); + const context = makeActionContext({ + field: { name: fieldName, value, type: 'text' }, + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return display name', () => { + expect(filterInAction.getDisplayName(context)).toEqual('Filter In'); + }); + + it('should return icon type', () => { + expect(filterInAction.getIconType(context)).toEqual('plusInCircle'); + }); + + describe('isCompatible', () => { + it('should return true if everything is okay', async () => { + expect(await filterInAction.isCompatible(context)).toEqual(true); + }); + + it('should return false if field.name not valid', async () => { + expect( + await filterInAction.isCompatible({ + ...context, + field: { ...context.field, name: '' }, + }) + ).toEqual(false); + }); + }); + + describe('execute', () => { + it('should add the filter to filterManager', async () => { + await filterInAction.execute(context); + expect(mockFilterManager.addFilters).toHaveBeenCalled(); + }); + + it('should create filter query with value', async () => { + await filterInAction.execute(context); + expect(mockCreateFilter).toHaveBeenCalledWith({ + key: fieldName, + value, + negate: false, + }); + }); + + it('should create filter query with array value', async () => { + await filterInAction.execute({ + ...context, + field: { ...context.field, value: [value] }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ + key: fieldName, + value: [value], + negate: false, + }); + }); + + it('should create negate filter query with null value', async () => { + await filterInAction.execute({ + ...context, + field: { ...context.field, value: null }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: null, negate: true }); + }); + + it('should create negate filter query with undefined value', async () => { + await filterInAction.execute({ + ...context, + field: { ...context.field, value: undefined }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ + key: fieldName, + value: undefined, + negate: true, + }); + }); + + it('should create negate filter query with empty string value', async () => { + await filterInAction.execute({ + ...context, + field: { ...context.field, value: '' }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: '', negate: true }); + }); + + it('should create negate filter query with empty array value', async () => { + await filterInAction.execute({ + ...context, + field: { ...context.field, value: [] }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [], negate: true }); + }); + }); +}); diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_in.ts b/packages/kbn-cell-actions/src/actions/filter/filter_in.ts new file mode 100644 index 0000000000000..524318d9722d1 --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/filter/filter_in.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { i18n } from '@kbn/i18n'; +import type { FilterManager } from '@kbn/data-plugin/public'; +import { createFilter, isEmptyFilterValue } from './create_filter'; +import { FILTER_CELL_ACTION_TYPE } from '../../constants'; +import { createCellActionFactory } from '../factory'; + +const ICON = 'plusInCircle'; +const FILTER_IN = i18n.translate('cellActions.actions.filterIn', { + defaultMessage: 'Filter In', +}); + +export const createFilterInActionFactory = createCellActionFactory( + ({ filterManager }: { filterManager: FilterManager }) => ({ + type: FILTER_CELL_ACTION_TYPE, + getIconType: () => ICON, + getDisplayName: () => FILTER_IN, + getDisplayNameTooltip: () => FILTER_IN, + isCompatible: async ({ field }) => !!field.name, + execute: async ({ field }) => { + addFilterIn({ filterManager, fieldName: field.name, value: field.value }); + }, + }) +); + +export const addFilterIn = ({ + filterManager, + fieldName, + value, +}: { + filterManager: FilterManager | undefined; + fieldName: string; + value: string[] | string | null | undefined; +}) => { + if (filterManager != null) { + const filter = createFilter({ + key: fieldName, + value, + negate: isEmptyFilterValue(value), + }); + filterManager.addFilters(filter); + } +}; diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts b/packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts new file mode 100644 index 0000000000000..72564d64fc8bf --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { FilterManager } from '@kbn/data-plugin/public'; +import { createFilterOutActionFactory } from './filter_out'; +import { makeActionContext } from '../../mocks/helpers'; + +const mockFilterManager = { addFilters: jest.fn() } as unknown as FilterManager; + +const mockCreateFilter = jest.fn((_: any) => ({})); +jest.mock('./create_filter', () => ({ + ...jest.requireActual('./create_filter'), + createFilter: (params: any) => mockCreateFilter(params), +})); + +const fieldName = 'user.name'; +const value = 'the value'; + +describe('createFilterOutAction', () => { + const filterOutActionFactory = createFilterOutActionFactory({ filterManager: mockFilterManager }); + const filterOutAction = filterOutActionFactory({ id: 'testAction' }); + const context = makeActionContext({ + field: { name: fieldName, value, type: 'text' }, + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return display name', () => { + expect(filterOutAction.getDisplayName(context)).toEqual('Filter Out'); + }); + + it('should return icon type', () => { + expect(filterOutAction.getIconType(context)).toEqual('minusInCircle'); + }); + + describe('isCompatible', () => { + it('should return true if everything is okay', async () => { + expect(await filterOutAction.isCompatible(context)).toEqual(true); + }); + + it('should return false if field.name not valid', async () => { + expect( + await filterOutAction.isCompatible({ + ...context, + field: { ...context.field, name: '' }, + }) + ).toEqual(false); + }); + }); + + describe('execute', () => { + it('should add the filter to filterManager', async () => { + await filterOutAction.execute(context); + expect(mockFilterManager.addFilters).toHaveBeenCalled(); + }); + + it('should create negate filter query with value', async () => { + await filterOutAction.execute(context); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value, negate: true }); + }); + + it('should create negate filter query with array value', async () => { + await filterOutAction.execute({ + ...context, + field: { ...context.field, value: [value] }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ + key: fieldName, + value: [value], + negate: true, + }); + }); + + it('should create filter query with null value', async () => { + await filterOutAction.execute({ + ...context, + field: { ...context.field, value: null }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: null, negate: false }); + }); + + it('should create filter query with undefined value', async () => { + await filterOutAction.execute({ + ...context, + field: { ...context.field, value: undefined }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ + key: fieldName, + value: undefined, + negate: false, + }); + }); + + it('should create negate filter query with empty string value', async () => { + await filterOutAction.execute({ + ...context, + field: { ...context.field, value: '' }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: '', negate: false }); + }); + + it('should create negate filter query with empty array value', async () => { + await filterOutAction.execute({ + ...context, + field: { ...context.field, value: [] }, + }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [], negate: false }); + }); + }); +}); diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_out.ts b/packages/kbn-cell-actions/src/actions/filter/filter_out.ts new file mode 100644 index 0000000000000..bbb1b7ee70a61 --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/filter/filter_out.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { i18n } from '@kbn/i18n'; +import type { FilterManager } from '@kbn/data-plugin/public'; +import { createFilter, isEmptyFilterValue } from './create_filter'; +import { FILTER_CELL_ACTION_TYPE } from '../../constants'; +import { createCellActionFactory } from '../factory'; + +const ICON = 'minusInCircle'; +const FILTER_OUT = i18n.translate('cellActions.actions.filterOut', { + defaultMessage: 'Filter Out', +}); + +export const createFilterOutActionFactory = createCellActionFactory( + ({ filterManager }: { filterManager: FilterManager }) => ({ + type: FILTER_CELL_ACTION_TYPE, + getIconType: () => ICON, + getDisplayName: () => FILTER_OUT, + getDisplayNameTooltip: () => FILTER_OUT, + isCompatible: async ({ field }) => !!field.name, + execute: async ({ field }) => { + addFilterOut({ + filterManager, + fieldName: field.name, + value: field.value, + }); + }, + }) +); + +export const addFilterOut = ({ + filterManager, + fieldName, + value, +}: { + filterManager: FilterManager | undefined; + fieldName: string; + value: string[] | string | null | undefined; +}) => { + if (filterManager != null) { + const filter = createFilter({ + key: fieldName, + value, + negate: !isEmptyFilterValue(value), + }); + filterManager.addFilters(filter); + } +}; diff --git a/packages/kbn-cell-actions/src/actions/filter/index.ts b/packages/kbn-cell-actions/src/actions/filter/index.ts new file mode 100644 index 0000000000000..19a32c05db6cb --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/filter/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { createFilterInActionFactory, addFilterIn } from './filter_in'; +export { createFilterOutActionFactory, addFilterOut } from './filter_out'; diff --git a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx index c7b685da3e097..ec266889fe255 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx @@ -9,7 +9,7 @@ import { act, render } from '@testing-library/react'; import React from 'react'; import { CellActions } from './cell_actions'; -import { CellActionsMode } from '../types'; +import { CellActionsMode } from '../constants'; import { CellActionsProvider } from '../context/cell_actions_context'; const TRIGGER_ID = 'test-trigger-id'; diff --git a/packages/kbn-cell-actions/src/components/cell_actions.tsx b/packages/kbn-cell-actions/src/components/cell_actions.tsx index 3d843a3168f79..f6d3288ed6f7d 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.tsx @@ -10,7 +10,8 @@ import React, { useMemo, useRef } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { InlineActions } from './inline_actions'; import { HoverActionsPopover } from './hover_actions_popover'; -import { CellActionsMode, type CellActionsProps, type CellActionExecutionContext } from '../types'; +import { CellActionsMode } from '../constants'; +import type { CellActionsProps, CellActionExecutionContext } from '../types'; export const CellActions: React.FC = ({ field, @@ -19,7 +20,7 @@ export const CellActions: React.FC = ({ mode, showActionTooltips = true, visibleCellActions = 3, - disabledActions = [], + disabledActionTypes = [], metadata, className, }) => { @@ -43,7 +44,7 @@ export const CellActions: React.FC = ({ actionContext={actionContext} showActionTooltips={showActionTooltips} visibleCellActions={visibleCellActions} - disabledActions={disabledActions} + disabledActionTypes={disabledActionTypes} > {children} @@ -67,7 +68,7 @@ export const CellActions: React.FC = ({ actionContext={actionContext} showActionTooltips={showActionTooltips} visibleCellActions={visibleCellActions} - disabledActions={disabledActions} + disabledActionTypes={disabledActionTypes} /> diff --git a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx index 7c513d2eb8515..b30ca63e52ec0 100644 --- a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx +++ b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx @@ -26,7 +26,7 @@ describe('HoverActionsPopover', () => { const { queryByTestId } = render( { const { queryByLabelText, getByTestId } = render( { const { queryByLabelText, getByTestId } = render( { const { getByTestId } = render( { const { getByTestId, getByLabelText } = render( { const { getByTestId, queryByLabelText } = render( = ({ @@ -48,13 +48,13 @@ export const HoverActionsPopover: React.FC = ({ visibleCellActions, actionContext, showActionTooltips, - disabledActions, + disabledActionTypes, }) => { const contentRef = useRef(null); const [isExtraActionsPopoverOpen, setIsExtraActionsPopoverOpen] = useState(false); const [showHoverContent, setShowHoverContent] = useState(false); - const [{ value: actions }, loadActions] = useLoadActionsFn({ disabledActions }); + const [{ value: actions }, loadActions] = useLoadActionsFn({ disabledActionTypes }); const { visibleActions, extraActions } = useMemo( () => partitionActions(actions ?? [], visibleCellActions), diff --git a/packages/kbn-cell-actions/src/components/inline_actions.test.tsx b/packages/kbn-cell-actions/src/components/inline_actions.test.tsx index 2c3423c021e65..eaba9ec2b0d8b 100644 --- a/packages/kbn-cell-actions/src/components/inline_actions.test.tsx +++ b/packages/kbn-cell-actions/src/components/inline_actions.test.tsx @@ -21,7 +21,7 @@ describe('InlineActions', () => { const { queryByTestId } = render( { const { queryAllByRole } = render( = ({ actionContext, showActionTooltips, visibleCellActions, - disabledActions, + disabledActionTypes, }) => { - const { value: actions } = useLoadActions(actionContext, { disabledActions }); + const { value: actions } = useLoadActions(actionContext, { disabledActionTypes }); const { extraActions, visibleActions } = usePartitionActions(actions ?? [], visibleCellActions); const [isPopoverOpen, setIsPopoverOpen] = useState(false); diff --git a/packages/kbn-cell-actions/src/constants.ts b/packages/kbn-cell-actions/src/constants.ts new file mode 100644 index 0000000000000..86eb81ed5c7cf --- /dev/null +++ b/packages/kbn-cell-actions/src/constants.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 FILTER_CELL_ACTION_TYPE = 'cellAction-filter'; +export const COPY_CELL_ACTION_TYPE = 'cellAction-copy'; + +export enum CellActionsMode { + HOVER = 'hover', + INLINE = 'inline', +} diff --git a/packages/kbn-cell-actions/src/hooks/index.ts b/packages/kbn-cell-actions/src/hooks/index.ts index 94086c432065e..18bdb9d4053c5 100644 --- a/packages/kbn-cell-actions/src/hooks/index.ts +++ b/packages/kbn-cell-actions/src/hooks/index.ts @@ -9,4 +9,5 @@ export { useDataGridColumnsCellActions, type UseDataGridColumnsCellActionsProps, + type UseDataGridColumnsCellActions, } from './use_data_grid_column_cell_actions'; diff --git a/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx b/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx index 2fe9668097f1b..344e4a058ad44 100644 --- a/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx +++ b/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx @@ -29,17 +29,21 @@ interface BulkField extends Pick { } export interface UseDataGridColumnsCellActionsProps - extends Pick { + extends Pick { fields: BulkField[]; dataGridRef: MutableRefObject; } -export const useDataGridColumnsCellActions = ({ +export type UseDataGridColumnsCellActions< + P extends UseDataGridColumnsCellActionsProps = UseDataGridColumnsCellActionsProps +> = (props: P) => EuiDataGridColumnCellAction[][]; + +export const useDataGridColumnsCellActions: UseDataGridColumnsCellActions = ({ fields, triggerId, metadata, dataGridRef, - disabledActions = [], -}: UseDataGridColumnsCellActionsProps): EuiDataGridColumnCellAction[][] => { + disabledActionTypes = [], +}) => { const bulkContexts: CellActionCompatibilityContext[] = useMemo( () => fields.map(({ values, ...field }) => ({ @@ -50,7 +54,9 @@ export const useDataGridColumnsCellActions = ({ [fields, triggerId, metadata] ); - const { loading, value: columnsActions } = useBulkLoadActions(bulkContexts, { disabledActions }); + const { loading, value: columnsActions } = useBulkLoadActions(bulkContexts, { + disabledActionTypes, + }); const columnsCellActions = useMemo(() => { if (loading) { diff --git a/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts b/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts index e9612e6ffa26d..8df9102719466 100644 --- a/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts +++ b/packages/kbn-cell-actions/src/hooks/use_load_actions.test.ts @@ -58,7 +58,7 @@ describe('loadActions hooks', () => { mockGetActions.mockResolvedValue([actionEnabled, actionDisabled]); const { result, waitForNextUpdate } = renderHook(() => - useLoadActions(actionContext, { disabledActions: [actionDisabled.id] }) + useLoadActions(actionContext, { disabledActionTypes: [actionDisabled.type] }) ); await waitForNextUpdate(); @@ -110,13 +110,13 @@ describe('loadActions hooks', () => { expect(result.error?.message).toEqual(message); }); - it('filters out disabled actions', async () => { + it('filters out disabled actions types', async () => { const actionEnabled = makeAction('action-enabled'); const actionDisabled = makeAction('action-disabled'); mockGetActions.mockResolvedValue([actionEnabled, actionDisabled]); const { result, waitForNextUpdate } = renderHook(() => - useLoadActionsFn({ disabledActions: [actionDisabled.id] }) + useLoadActionsFn({ disabledActionTypes: [actionDisabled.type] }) ); const [_, loadActions] = result.current; @@ -163,13 +163,13 @@ describe('loadActions hooks', () => { expect(result.error?.message).toEqual(message); }); - it('filters out disabled actions', async () => { + it('filters out disabled actions types', async () => { const actionEnabled = makeAction('action-enabled'); const actionDisabled = makeAction('action-disabled'); mockGetActions.mockResolvedValue([actionEnabled, actionDisabled]); const { result, waitForNextUpdate } = renderHook(() => - useBulkLoadActions(actionContexts, { disabledActions: [actionDisabled.id] }) + useBulkLoadActions(actionContexts, { disabledActionTypes: [actionDisabled.type] }) ); await waitForNextUpdate(); diff --git a/packages/kbn-cell-actions/src/hooks/use_load_actions.ts b/packages/kbn-cell-actions/src/hooks/use_load_actions.ts index a1d84e9dd2ade..54e6843bac279 100644 --- a/packages/kbn-cell-actions/src/hooks/use_load_actions.ts +++ b/packages/kbn-cell-actions/src/hooks/use_load_actions.ts @@ -29,7 +29,7 @@ export const useLoadActions = ( ): AsyncActions => { const { getActions } = useCellActionsContext(); const { error, value, loading } = useAsync(() => getActions(context), []); - const filteredActions = useFilteredActions(value, options.disabledActions); + const filteredActions = useFilteredActions(value, options.disabledActionTypes); useThrowError(error); return { value: filteredActions, loading }; }; @@ -40,13 +40,13 @@ export const useLoadActions = ( export const useLoadActionsFn = (options: LoadActionsOptions = {}): [AsyncActions, GetActions] => { const { getActions } = useCellActionsContext(); const [{ error, value, loading }, loadActions] = useAsyncFn(getActions, []); - const filteredActions = useFilteredActions(value, options.disabledActions); + const filteredActions = useFilteredActions(value, options.disabledActionTypes); useThrowError(error); return [{ value: filteredActions, loading }, loadActions]; }; interface LoadActionsOptions { - disabledActions?: string[]; + disabledActionTypes?: string[]; } /** @@ -62,7 +62,7 @@ export const useBulkLoadActions = ( Promise.all( contexts.map((context) => getActions(context).then( - (actions) => filteredActions(actions, options.disabledActions) ?? [] + (actions) => filteredActions(actions, options.disabledActionTypes) ?? [] ) ) ), @@ -72,8 +72,8 @@ export const useBulkLoadActions = ( return actionsState; }; -const useFilteredActions = (actions: CellAction[] | undefined, disabledActions?: string[]) => - useMemo(() => filteredActions(actions, disabledActions), [actions, disabledActions]); +const useFilteredActions = (actions: CellAction[] | undefined, disabledActionTypes?: string[]) => + useMemo(() => filteredActions(actions, disabledActionTypes), [actions, disabledActionTypes]); -const filteredActions = (actions: CellAction[] | undefined, disabledActions: string[] = []) => - actions ? actions.filter(({ id }) => !disabledActions?.includes(id)) : undefined; +const filteredActions = (actions: CellAction[] | undefined, disabledActionTypes: string[] = []) => + actions ? actions.filter(({ type }) => !disabledActionTypes?.includes(type)) : undefined; diff --git a/packages/kbn-cell-actions/src/index.ts b/packages/kbn-cell-actions/src/index.ts index 61c74c7c92621..7c13b97adf276 100644 --- a/packages/kbn-cell-actions/src/index.ts +++ b/packages/kbn-cell-actions/src/index.ts @@ -6,12 +6,34 @@ * Side Public License, v 1. */ -export { CellActions } from './components'; -export { CellActionsProvider } from './context'; -export { useDataGridColumnsCellActions, type UseDataGridColumnsCellActionsProps } from './hooks'; -export { CellActionsMode } from './types'; +// Types and enums export type { CellAction, + CellActionsProps, CellActionExecutionContext, CellActionCompatibilityContext, + CellActionTemplate, + CellActionFactory, + CellActionExtend, } from './types'; +export type { UseDataGridColumnsCellActions, UseDataGridColumnsCellActionsProps } from './hooks'; + +// Constants +export { CellActionsMode, FILTER_CELL_ACTION_TYPE, COPY_CELL_ACTION_TYPE } from './constants'; + +// Components and hooks +export { CellActionsProvider } from './context'; +export { CellActions } from './components'; +export { useDataGridColumnsCellActions } from './hooks'; + +// Generic actions +export { createCopyToClipboardActionFactory } from './actions/copy_to_clipboard'; +export { + createFilterInActionFactory, + createFilterOutActionFactory, + addFilterIn, + addFilterOut, +} from './actions/filter'; + +// Action factory +export { createCellActionFactory } from './actions/factory'; diff --git a/packages/kbn-cell-actions/src/mocks/helpers.ts b/packages/kbn-cell-actions/src/mocks/helpers.ts index acb1afd1bc21e..46fd53448f512 100644 --- a/packages/kbn-cell-actions/src/mocks/helpers.ts +++ b/packages/kbn-cell-actions/src/mocks/helpers.ts @@ -33,5 +33,6 @@ export const makeActionContext = ( value: 'some value', }, nodeRef: {} as MutableRefObject, + metadata: undefined, ...override, }); diff --git a/packages/kbn-cell-actions/src/types.ts b/packages/kbn-cell-actions/src/types.ts index bb58ebdc69f51..1efc4239514f7 100644 --- a/packages/kbn-cell-actions/src/types.ts +++ b/packages/kbn-cell-actions/src/types.ts @@ -5,12 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import type { Action, ActionExecutionContext, UiActionsService, } from '@kbn/ui-actions-plugin/public'; +import type { CellActionsMode } from './constants'; export interface CellActionsProviderProps { /** @@ -46,10 +46,7 @@ export interface CellActionField { aggregatable?: boolean; } -export enum CellActionsMode { - HOVER = 'hover', - INLINE = 'inline', -} +type Metadata = Record; export interface CellActionsProps { /** @@ -80,19 +77,17 @@ export interface CellActionsProps { /** * List of Actions ids that shouldn't be displayed inside cell actions. */ - disabledActions?: string[]; + disabledActionTypes?: string[]; /** * Custom set of properties used by some actions. * An action might require a specific set of metadata properties to render. * This data is sent directly to actions. */ - metadata?: Record; + metadata?: Metadata; className?: string; } -type Metadata = Record | undefined; - export interface CellActionExecutionContext extends ActionExecutionContext { field: CellActionField; /** @@ -102,19 +97,24 @@ export interface CellActionExecutionContext extends ActionExecutionContext { /** * Extra configurations for actions. */ - metadata?: Metadata; + metadata: Metadata | undefined; } -export interface CellActionCompatibilityContext - extends ActionExecutionContext { +/** + * Subset of `CellActionExecutionContext` used only for the compatibility check in the `isCompatible` function. + * It omits the references and the `field.value`. + */ +export interface CellActionCompatibilityContext< + C extends CellActionExecutionContext = CellActionExecutionContext +> extends ActionExecutionContext { /** * The object containing the field name and type, needed for the compatibility check */ - field: Pick; + field: Omit; /** * Extra configurations for actions. */ - metadata?: M; + metadata: C['metadata'] | undefined; } export interface CellAction @@ -123,7 +123,7 @@ export interface CellAction): Promise; + isCompatible(context: CellActionCompatibilityContext): Promise; } export type GetActions = (context: CellActionCompatibilityContext) => Promise; @@ -132,3 +132,20 @@ export interface PartitionedActions { extraActions: CellAction[]; visibleActions: CellAction[]; } + +/** + * Cell action factory template with optional `id`. + * The id override is required when using the action factory so it + * can be omitted in the original action creator + */ +export type CellActionTemplate = Omit; +/** + * Action factory extend parameter type, + */ +export type CellActionExtend = Partial & { id: string }; +export interface CellActionFactory { + (extend: CellActionExtend): A; + combine: ( + partialActionTemplate: Partial> + ) => CellActionFactory; +} diff --git a/packages/kbn-cell-actions/tsconfig.json b/packages/kbn-cell-actions/tsconfig.json index 63c76dfbfaa45..cc40d4c80f3b0 100644 --- a/packages/kbn-cell-actions/tsconfig.json +++ b/packages/kbn-cell-actions/tsconfig.json @@ -15,6 +15,9 @@ "kbn_references": [ "@kbn/ui-theme", "@kbn/i18n", + "@kbn/core", + "@kbn/data-plugin", + "@kbn/es-query", "@kbn/ui-actions-plugin", ], "exclude": ["target/**/*"] diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index d4df7e45c67be..0a100ba2870b7 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -498,7 +498,3 @@ export const DEFAULT_DETECTION_PAGE_FILTERS = [ fieldName: 'host.name', }, ]; - -export const CELL_ACTIONS_DEFAULT_TRIGGER = 'security-solution-default-cellActions'; -export const CELL_ACTIONS_TIMELINE_TRIGGER = 'security-solution-timeline-cellActions'; -export const CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER = 'security-solution-details-flyout-cellActions'; diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index 1b5ca92e1eb1c..eb2cb28d80844 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -138,15 +138,15 @@ export const EVENT_CONTAINER_TABLE_NOT_LOADING = export const FILTER_BADGE = '[data-test-subj^="filter-badge"]'; export const CELL_FILTER_IN_BUTTON = - '[data-test-subj="dataGridColumnCellAction-security_filterIn"]'; + '[data-test-subj="dataGridColumnCellAction-security-default-cellActions-filterIn"]'; export const CELL_FILTER_OUT_BUTTON = - '[data-test-subj="dataGridColumnCellAction-security_filterOut"]'; + '[data-test-subj="dataGridColumnCellAction-security-default-cellActions-filterOut"]'; export const CELL_ADD_TO_TIMELINE_BUTTON = - '[data-test-subj="dataGridColumnCellAction-security_addToTimeline"]'; + '[data-test-subj="dataGridColumnCellAction-security-default-cellActions-addToTimeline"]'; export const CELL_SHOW_TOP_FIELD_BUTTON = - '[data-test-subj="dataGridColumnCellAction-security_showTopN"]'; + '[data-test-subj="dataGridColumnCellAction-security-default-cellActions-showTopN"]'; export const CELL_COPY_BUTTON = - '[data-test-subj="dataGridColumnCellAction-security_copyToClipboard"]'; + '[data-test-subj="dataGridColumnCellAction-security-default-cellActions-copyToClipboard"]'; export const ACTIONS_EXPAND_BUTTON = '[data-test-subj="euiDataGridCellExpandButton"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/network/flows.ts b/x-pack/plugins/security_solution/cypress/screens/network/flows.ts index d20e1d69199af..e29d2a85a69a7 100644 --- a/x-pack/plugins/security_solution/cypress/screens/network/flows.ts +++ b/x-pack/plugins/security_solution/cypress/screens/network/flows.ts @@ -9,15 +9,16 @@ export const IPS_TABLE_LOADED = '[data-test-subj="table-topNFlowSource-loading-f export const EXPAND_OVERFLOW_ITEMS = '[data-test-subj="overflow-button"]'; -export const FILTER_IN = '[data-test-subj="actionItem-security_filterIn"]'; +export const FILTER_IN = '[data-test-subj="actionItem-security-default-cellActions-filterIn"]'; -export const FILTER_OUT = '[data-test-subj="actionItem-security_filterOut"]'; +export const FILTER_OUT = '[data-test-subj="actionItem-security-default-cellActions-filterOut"]'; -export const ADD_TO_TIMELINE = '[data-test-subj="actionItem-security_addToTimeline"]'; +export const ADD_TO_TIMELINE = + '[data-test-subj="actionItem-security-default-cellActions-addToTimeline"]'; -export const SHOW_TOP_FIELD = '[data-test-subj="actionItem-security_showTopN"]'; +export const SHOW_TOP_FIELD = '[data-test-subj="actionItem-security-default-cellActions-showTopN"]'; -export const COPY = '[data-test-subj="actionItem-security_copyToClipboard"]'; +export const COPY = '[data-test-subj="actionItem-security-default-cellActions-copyToClipboard"]'; export const TOP_N_CONTAINER = '[data-test-subj="topN-container"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index a9cdb896cdea4..2752afad6323a 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -60,7 +60,8 @@ export const HOST_KPI = '[data-test-subj="siem-timeline-host-kpi"]'; export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]'; -export const ID_TOGGLE_FIELD = '[data-test-subj="actionItem-security_toggleColumn"]'; +export const ID_TOGGLE_FIELD = + '[data-test-subj="actionItem-security-detailsFlyout-cellActions-toggleColumn"]'; export const ID_HOVER_ACTION_OVERFLOW_BTN = '[data-test-subj="event-fields-table-row-_id"] [data-test-subj="showExtraActionsButton"]'; @@ -238,7 +239,8 @@ export const TIMELINE_TITLE_INPUT = '[data-test-subj="save-timeline-title"]'; export const TIMESTAMP_HEADER_FIELD = '[data-test-subj="header-text-@timestamp"]'; -export const TIMESTAMP_TOGGLE_FIELD = '[data-test-subj="actionItem-security_toggleColumn"]'; +export const TIMESTAMP_TOGGLE_FIELD = + '[data-test-subj="actionItem-security-detailsFlyout-cellActions-toggleColumn"]'; export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]'; @@ -296,9 +298,6 @@ export const ALERT_TABLE_FILE_NAME_HEADER = '[data-gridcell-column-id="file.name export const ALERT_TABLE_FILE_NAME_VALUES = '[data-gridcell-column-id="file.name"][data-test-subj="dataGridRowCell"]'; // empty column for the test data -export const ALERT_TABLE_CELL_ACTIONS_ADD_TO_TIMELINE = - '[data-test-subj="dataGridColumnCellAction-security_addToTimeline"]'; - export const ACTIVE_TIMELINE_BOTTOM_BAR = '[data-test-subj="flyoutBottomBar"] .active-timeline-button'; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/default/add_to_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts similarity index 80% rename from x-pack/plugins/security_solution/public/actions/add_to_timeline/default/add_to_timeline.test.ts rename to x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts index 7fe791cc333a0..340a7cc11c2e2 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/default/add_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts @@ -6,20 +6,15 @@ */ import type { SecurityAppStore } from '../../../common/store/types'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { APP_UI_ID } from '../../../../common/constants'; -import { Subject } from 'rxjs'; import { TimelineId } from '../../../../common/types'; import { addProvider } from '../../../timelines/store/timeline/actions'; -import { createAddToTimelineAction } from './add_to_timeline'; +import { createAddToTimelineCellActionFactory } from './add_to_timeline'; import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; +import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; -jest.mock('../../../common/lib/kibana'); -const currentAppId$ = new Subject(); -KibanaServices.get().application.currentAppId$ = currentAppId$.asObservable(); -const mockWarningToast = jest.fn(); -KibanaServices.get().notifications.toasts.addWarning = mockWarningToast; +const services = createStartServicesMock(); +const mockWarningToast = services.notifications.toasts.addWarning; const mockDispatch = jest.fn(); const store = { @@ -54,11 +49,11 @@ const defaultDataProvider = { }, }; -describe('Default createAddToTimelineAction', () => { - const addToTimelineAction = createAddToTimelineAction({ store, order: 1 }); +describe('createAddToTimelineCellAction', () => { + const addToTimelineCellActionFactory = createAddToTimelineCellActionFactory({ store, services }); + const addToTimelineAction = addToTimelineCellActionFactory({ id: 'testAddToTimeline', order: 1 }); beforeEach(() => { - currentAppId$.next(APP_UI_ID); jest.clearAllMocks(); }); @@ -71,14 +66,17 @@ describe('Default createAddToTimelineAction', () => { }); describe('isCompatible', () => { - it('should return false if not in Security', async () => { - currentAppId$.next('not security'); - expect(await addToTimelineAction.isCompatible(context)).toEqual(false); - }); - it('should return true if everything is okay', async () => { expect(await addToTimelineAction.isCompatible(context)).toEqual(true); }); + it('should return false if field not allowed', async () => { + expect( + await addToTimelineAction.isCompatible({ + ...context, + field: { ...context.field, name: 'signal.reason' }, + }) + ).toEqual(false); + }); }); describe('execute', () => { diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts new file mode 100644 index 0000000000000..c444a46cd6407 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createCellActionFactory } from '@kbn/cell-actions'; +import type { CellActionTemplate } from '@kbn/cell-actions'; +import { addProvider } from '../../../timelines/store/timeline/actions'; +import { TimelineId } from '../../../../common/types'; +import type { SecurityAppStore } from '../../../common/store'; +import { fieldHasCellActions } from '../../utils'; +import { + ADD_TO_TIMELINE, + ADD_TO_TIMELINE_FAILED_TEXT, + ADD_TO_TIMELINE_FAILED_TITLE, + ADD_TO_TIMELINE_ICON, + ADD_TO_TIMELINE_SUCCESS_TITLE, +} from '../constants'; +import { createDataProviders } from '../data_provider'; +import { SecurityCellActionType } from '../../constants'; +import type { StartServices } from '../../../types'; +import type { SecurityCellAction } from '../../types'; + +export const createAddToTimelineCellActionFactory = createCellActionFactory( + ({ + store, + services, + }: { + store: SecurityAppStore; + services: StartServices; + }): CellActionTemplate => { + const { notifications: notificationsService } = services; + + return { + type: SecurityCellActionType.ADD_TO_TIMELINE, + getIconType: () => ADD_TO_TIMELINE_ICON, + getDisplayName: () => ADD_TO_TIMELINE, + getDisplayNameTooltip: () => ADD_TO_TIMELINE, + isCompatible: async ({ field }) => fieldHasCellActions(field.name), + execute: async ({ field, metadata }) => { + const dataProviders = + createDataProviders({ + contextId: TimelineId.active, + fieldType: field.type, + values: field.value, + field: field.name, + negate: metadata?.negateFilters === true, + }) ?? []; + + if (dataProviders.length > 0) { + store.dispatch(addProvider({ id: TimelineId.active, providers: dataProviders })); + + let messageValue = ''; + if (field.value != null) { + messageValue = Array.isArray(field.value) ? field.value.join(', ') : field.value; + } + notificationsService.toasts.addSuccess({ + title: ADD_TO_TIMELINE_SUCCESS_TITLE(messageValue), + }); + } else { + notificationsService.toasts.addWarning({ + title: ADD_TO_TIMELINE_FAILED_TITLE, + text: ADD_TO_TIMELINE_FAILED_TEXT, + }); + } + }, + }; + } +); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/default/add_to_timeline.tsx b/x-pack/plugins/security_solution/public/actions/add_to_timeline/default/add_to_timeline.tsx deleted file mode 100644 index 5bb23be732750..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/default/add_to_timeline.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CellAction } from '@kbn/cell-actions'; -import { addProvider } from '../../../timelines/store/timeline/actions'; -import { TimelineId } from '../../../../common/types'; -import { KibanaServices } from '../../../common/lib/kibana'; -import type { SecurityAppStore } from '../../../common/store'; -import { fieldHasCellActions, isInSecurityApp } from '../../utils'; -import { - ADD_TO_TIMELINE, - ADD_TO_TIMELINE_FAILED_TEXT, - ADD_TO_TIMELINE_FAILED_TITLE, - ADD_TO_TIMELINE_ICON, - ADD_TO_TIMELINE_SUCCESS_TITLE, -} from '../constants'; -import { createDataProviders } from '../data_provider'; - -export const ACTION_ID = 'security_addToTimeline'; - -export const createAddToTimelineAction = ({ - store, - order, -}: { - store: SecurityAppStore; - order?: number; -}): CellAction => { - const { application: applicationService, notifications: notificationsService } = - KibanaServices.get(); - let currentAppId: string | undefined; - applicationService.currentAppId$.subscribe((appId) => { - currentAppId = appId; - }); - - return { - id: ACTION_ID, - type: ACTION_ID, - order, - getIconType: (): string => ADD_TO_TIMELINE_ICON, - getDisplayName: () => ADD_TO_TIMELINE, - getDisplayNameTooltip: () => ADD_TO_TIMELINE, - isCompatible: async ({ field }) => - isInSecurityApp(currentAppId) && fieldHasCellActions(field.name), - execute: async ({ field, metadata }) => { - const negate = Boolean(metadata?.negateFilters); - const dataProviders = - createDataProviders({ - contextId: TimelineId.active, - fieldType: field.type, - values: field.value, - field: field.name, - negate, - }) ?? []; - - if (dataProviders.length > 0) { - store.dispatch(addProvider({ id: TimelineId.active, providers: dataProviders })); - - let messageValue = ''; - if (field.value != null) { - messageValue = Array.isArray(field.value) ? field.value.join(', ') : field.value; - } - notificationsService.toasts.addSuccess({ - title: ADD_TO_TIMELINE_SUCCESS_TITLE(messageValue), - }); - } else { - notificationsService.toasts.addWarning({ - title: ADD_TO_TIMELINE_FAILED_TITLE, - text: ADD_TO_TIMELINE_FAILED_TEXT, - }); - } - }, - }; -}; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts index d948e0c65acec..c639dde1e2337 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { createAddToTimelineAction as createDefaultAddToTimelineAction } from './default/add_to_timeline'; -export { createAddToTimelineAction as createLensAddToTimelineAction } from './lens/add_to_timeline'; +export { createAddToTimelineCellActionFactory } from './cell_action/add_to_timeline'; +export { createAddToTimelineLensAction } from './lens/add_to_timeline'; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts index 9b845a20f831f..9f172d4e18b3e 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts @@ -9,7 +9,7 @@ import type { CellValueContext, EmbeddableInput, IEmbeddable } from '@kbn/embedd import { ErrorEmbeddable } from '@kbn/embeddable-plugin/public'; import { LENS_EMBEDDABLE_TYPE } from '@kbn/lens-plugin/public'; import type { SecurityAppStore } from '../../../common/store/types'; -import { createAddToTimelineAction } from './add_to_timeline'; +import { createAddToTimelineLensAction } from './add_to_timeline'; import { KibanaServices } from '../../../common/lib/kibana'; import { APP_UI_ID } from '../../../../common/constants'; import { Subject } from 'rxjs'; @@ -54,8 +54,8 @@ const context = { embeddable: lensEmbeddable, } as unknown as ActionExecutionContext; -describe('Lens createAddToTimelineAction', () => { - const addToTimelineAction = createAddToTimelineAction({ store, order: 1 }); +describe('createAddToTimelineLensAction', () => { + const addToTimelineAction = createAddToTimelineLensAction({ store, order: 1 }); beforeEach(() => { currentAppId$.next(APP_UI_ID); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts index 892caff34b3dd..4efe1d2a0122a 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts @@ -38,7 +38,7 @@ function isDataColumnsFilterable(data?: CellValueContext['data']): boolean { ); } -export const createAddToTimelineAction = ({ +export const createAddToTimelineLensAction = ({ store, order, }: { diff --git a/x-pack/plugins/security_solution/public/actions/constants.ts b/x-pack/plugins/security_solution/public/actions/constants.ts new file mode 100644 index 0000000000000..94c2601222847 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/constants.ts @@ -0,0 +1,18 @@ +/* + * 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 enum SecurityCellActionsTrigger { + DEFAULT = 'security-default-cellActions', + DETAILS_FLYOUT = 'security-detailsFlyout-cellActions', +} + +export enum SecurityCellActionType { + FILTER = 'security-cellAction-type-filter', + COPY = 'security-cellAction-type-copyToClipboard', + ADD_TO_TIMELINE = 'security-cellAction-type-addToTimeline', + SHOW_TOP_N = 'security-cellAction-type-showTopN', + TOGGLE_COLUMN = 'security-cellAction-type-toggleColumn', +} diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts new file mode 100644 index 0000000000000..90ab55a5b0fd0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createCopyToClipboardActionFactory as genericCreateCopyToClipboardActionFactory } from '@kbn/cell-actions'; +import { fieldHasCellActions } from '../../utils'; +import type { StartServices } from '../../../types'; +import type { SecurityCellAction } from '../../types'; +import { SecurityCellActionType } from '../../constants'; + +export const createCopyToClipboardCellActionFactory = ({ + services, +}: { + services: StartServices; +}) => { + const { notifications } = services; + const genericCopyToClipboardActionFactory = genericCreateCopyToClipboardActionFactory({ + notifications, + }); + return genericCopyToClipboardActionFactory.combine({ + type: SecurityCellActionType.COPY, + isCompatible: async ({ field }) => fieldHasCellActions(field.name), + }); +}; diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default/copy_to_clipboard.tsx b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default/copy_to_clipboard.tsx deleted file mode 100644 index 9c0b65c42ff21..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/default/copy_to_clipboard.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CellAction } from '@kbn/cell-actions'; -import copy from 'copy-to-clipboard'; -import { COPY_TO_CLIPBOARD, COPY_TO_CLIPBOARD_ICON, COPY_TO_CLIPBOARD_SUCCESS } from '../constants'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { fieldHasCellActions } from '../../utils'; - -const ID = 'security_copyToClipboard'; - -export const createCopyToClipboardAction = ({ order }: { order?: number }): CellAction => ({ - id: ID, - type: ID, - order, - getIconType: (): string => COPY_TO_CLIPBOARD_ICON, - getDisplayName: () => COPY_TO_CLIPBOARD, - getDisplayNameTooltip: () => COPY_TO_CLIPBOARD, - isCompatible: async ({ field }) => fieldHasCellActions(field.name), - execute: async ({ field }) => { - const { notifications } = KibanaServices.get(); - - let textValue: undefined | string; - if (field.value != null) { - textValue = Array.isArray(field.value) - ? field.value.map((value) => `"${value}"`).join(', ') - : `"${field.value}"`; - } - const text = textValue ? `${field.name}: ${textValue}` : field.name; - const isSuccess = copy(text, { debug: true }); - - if (isSuccess) { - notifications.toasts.addSuccess( - { - title: COPY_TO_CLIPBOARD_SUCCESS, - }, - { - toastLifeTimeMs: 800, - } - ); - } - }, -}); diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/index.ts b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/index.ts index 07adcfba234fc..601e23086b422 100644 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/index.ts +++ b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { createCopyToClipboardAction as createLensCopyToClipboardAction } from './lens/copy_to_clipboard'; -export { createCopyToClipboardAction as createDefaultCopyToClipboardAction } from './default/copy_to_clipboard'; +export { createCopyToClipboardLensAction } from './lens/copy_to_clipboard'; +export { createCopyToClipboardCellActionFactory } from './cell_action/copy_to_clipboard'; diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts index 2b7f232514323..5e80ab4b00409 100644 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts +++ b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.test.ts @@ -8,7 +8,7 @@ import type { CellValueContext, EmbeddableInput, IEmbeddable } from '@kbn/embeddable-plugin/public'; import { ErrorEmbeddable } from '@kbn/embeddable-plugin/public'; import { LENS_EMBEDDABLE_TYPE } from '@kbn/lens-plugin/public'; -import { createCopyToClipboardAction } from './copy_to_clipboard'; +import { createCopyToClipboardLensAction } from './copy_to_clipboard'; import { KibanaServices } from '../../../common/lib/kibana'; import { APP_UI_ID } from '../../../../common/constants'; import { Subject } from 'rxjs'; @@ -46,8 +46,8 @@ const context = { embeddable: lensEmbeddable, } as unknown as ActionExecutionContext; -describe('Lens createCopyToClipboardAction', () => { - const copyToClipboardAction = createCopyToClipboardAction({ order: 1 }); +describe('createCopyToClipboardLensAction', () => { + const copyToClipboardAction = createCopyToClipboardLensAction({ order: 1 }); beforeEach(() => { currentAppId$.next(APP_UI_ID); diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.ts b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.ts index 0398a94ef98c2..1921434c0d4fa 100644 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.ts +++ b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/lens/copy_to_clipboard.ts @@ -23,7 +23,7 @@ function isDataColumnsValid(data?: CellValueContext['data']): boolean { ); } -export const createCopyToClipboardAction = ({ order }: { order?: number }) => { +export const createCopyToClipboardLensAction = ({ order }: { order?: number }) => { const { application: applicationService } = KibanaServices.get(); let currentAppId: string | undefined; applicationService.currentAppId$.subscribe((appId) => { diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts new file mode 100644 index 0000000000000..d9fa197f30bf1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts @@ -0,0 +1,134 @@ +/* + * 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 { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; +import { TableId, TimelineId } from '../../../../common/types'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, +} from '../../../common/mock'; +import { createStore } from '../../../common/store'; +import { createFilterInCellActionFactory } from './filter_in'; +import type { SecurityCellActionExecutionContext } from '../../types'; +import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; + +const services = createStartServicesMock(); +const mockFilterManager = services.data.query.filterManager; + +const mockState = { + ...mockGlobalState, + timeline: { + ...mockGlobalState.timeline, + timelineById: { + ...mockGlobalState.timeline.timelineById, + [TimelineId.active]: { + ...mockGlobalState.timeline.timelineById[TimelineId.active], + filterManager: createFilterManagerMock(), + }, + }, + }, +}; + +jest.mock('@kbn/ui-actions-plugin/public', () => ({ + ...jest.requireActual('@kbn/ui-actions-plugin/public'), + addFilterIn: () => {}, + addFilterOut: () => {}, +})); + +const { storage } = createSecuritySolutionStorageMock(); +const mockStore = createStore(mockState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + +describe('createFilterInCellActionFactory', () => { + const createFilterInCellAction = createFilterInCellActionFactory({ store: mockStore, services }); + const filterInAction = createFilterInCellAction({ id: 'testAction' }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + const context = { + field: { name: 'user.name', value: 'the value', type: 'text' }, + } as SecurityCellActionExecutionContext; + + it('should return display name', () => { + expect(filterInAction.getDisplayName(context)).toEqual('Filter In'); + }); + + it('should return icon type', () => { + expect(filterInAction.getIconType(context)).toEqual('plusInCircle'); + }); + + describe('isCompatible', () => { + it('should return true if everything is okay', async () => { + expect(await filterInAction.isCompatible(context)).toEqual(true); + }); + it('should return false if field not allowed', async () => { + expect( + await filterInAction.isCompatible({ + ...context, + field: { ...context.field, name: 'signal.reason' }, + }) + ).toEqual(false); + }); + }); + + describe('generic scope execution', () => { + const dataTableContext = { + ...context, + metadata: { scopeId: TableId.alertsOnAlertsPage }, + } as SecurityCellActionExecutionContext; + + it('should execute using generic filterManager', async () => { + await filterInAction.execute(dataTableContext); + expect(mockFilterManager.addFilters).toHaveBeenCalled(); + expect( + mockState.timeline.timelineById[TimelineId.active].filterManager?.addFilters + ).not.toHaveBeenCalled(); + }); + }); + + describe('timeline scope execution', () => { + const timelineContext = { + ...context, + metadata: { scopeId: TimelineId.active }, + } as SecurityCellActionExecutionContext; + + it('should execute using timeline filterManager', async () => { + await filterInAction.execute(timelineContext); + expect( + mockState.timeline.timelineById[TimelineId.active].filterManager?.addFilters + ).toHaveBeenCalled(); + expect(mockFilterManager.addFilters).not.toHaveBeenCalled(); + }); + + describe('should execute correctly when negateFilters is provided', () => { + it('if negateFilters is false, negate should be false (do not exclude)', async () => { + await filterInAction.execute({ + ...context, + metadata: { + negateFilters: false, + }, + }); + expect(mockFilterManager.addFilters).toHaveBeenCalled(); + // expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, false); + }); + + it('if negateFilters is true, negate should be true (exclude)', async () => { + await filterInAction.execute({ + ...context, + metadata: { + negateFilters: true, + }, + }); + expect(mockFilterManager.addFilters).toHaveBeenCalled(); + // expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, true); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts new file mode 100644 index 0000000000000..7b206b1f41cd1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts @@ -0,0 +1,57 @@ +/* + * 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 { addFilterIn, addFilterOut, createFilterInActionFactory } from '@kbn/cell-actions'; +import type { SecurityAppStore } from '../../../common/store'; +import { timelineSelectors } from '../../../timelines/store/timeline'; +import { fieldHasCellActions } from '../../utils'; +import { TimelineId } from '../../../../common/types'; +import { isTimelineScope } from '../../../helpers'; +import { SecurityCellActionType } from '../../constants'; +import type { StartServices } from '../../../types'; +import type { SecurityCellAction } from '../../types'; + +export const createFilterInCellActionFactory = ({ + store, + services, +}: { + store: SecurityAppStore; + services: StartServices; +}) => { + const getTimelineById = timelineSelectors.getTimelineByIdSelector(); + + const { filterManager } = services.data.query; + const genericFilterInActionFactory = createFilterInActionFactory({ filterManager }); + + return genericFilterInActionFactory.combine({ + type: SecurityCellActionType.FILTER, + isCompatible: async ({ field }) => fieldHasCellActions(field.name), + execute: async ({ field, metadata }) => { + // if negateFilters is true we have to perform the opposite operation, we can just execute filterOut with the same params + const addFilter = metadata?.negateFilters === true ? addFilterOut : addFilterIn; + + if (metadata?.scopeId && isTimelineScope(metadata.scopeId)) { + const timelineFilterManager = getTimelineById( + store.getState(), + TimelineId.active + )?.filterManager; + + addFilter({ + filterManager: timelineFilterManager, + fieldName: field.name, + value: field.value, + }); + } else { + addFilter({ + filterManager, + fieldName: field.name, + value: field.value, + }); + } + }, + }); +}; diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts new file mode 100644 index 0000000000000..f171b3084cbfa --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts @@ -0,0 +1,104 @@ +/* + * 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 { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; +import { TableId, TimelineId } from '../../../../common/types'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, +} from '../../../common/mock'; +import { createStore } from '../../../common/store'; +import { createFilterOutCellActionFactory } from './filter_out'; +import type { SecurityCellActionExecutionContext } from '../../types'; +import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; + +const services = createStartServicesMock(); +const mockFilterManager = services.data.query.filterManager; + +const mockState = { + ...mockGlobalState, + timeline: { + ...mockGlobalState.timeline, + timelineById: { + ...mockGlobalState.timeline.timelineById, + [TimelineId.active]: { + ...mockGlobalState.timeline.timelineById[TimelineId.active], + filterManager: createFilterManagerMock(), + }, + }, + }, +}; + +const { storage } = createSecuritySolutionStorageMock(); +const mockStore = createStore(mockState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + +describe('createFilterOutCellActionFactory', () => { + const filterOutActionFactory = createFilterOutCellActionFactory({ store: mockStore, services }); + const filterOutAction = filterOutActionFactory({ id: 'testAction' }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + const context = { + field: { name: 'user.name', value: 'the value', type: 'text' }, + } as SecurityCellActionExecutionContext; + + it('should return display name', () => { + expect(filterOutAction.getDisplayName(context)).toEqual('Filter Out'); + }); + + it('should return icon type', () => { + expect(filterOutAction.getIconType(context)).toEqual('minusInCircle'); + }); + + describe('isCompatible', () => { + it('should return true if everything is okay', async () => { + expect(await filterOutAction.isCompatible(context)).toEqual(true); + }); + it('should return false if field not allowed', async () => { + expect( + await filterOutAction.isCompatible({ + ...context, + field: { ...context.field, name: 'signal.reason' }, + }) + ).toEqual(false); + }); + }); + + describe('generic scope execution', () => { + const dataTableContext = { + ...context, + metadata: { scopeId: TableId.alertsOnAlertsPage }, + } as SecurityCellActionExecutionContext; + + it('should execute using generic filterManager', async () => { + await filterOutAction.execute(dataTableContext); + expect(mockFilterManager.addFilters).toHaveBeenCalled(); + expect( + mockState.timeline.timelineById[TimelineId.active].filterManager?.addFilters + ).not.toHaveBeenCalled(); + }); + }); + + describe('timeline scope execution', () => { + const timelineContext = { + ...context, + metadata: { scopeId: TimelineId.active }, + } as SecurityCellActionExecutionContext; + + it('should execute using timeline filterManager', async () => { + await filterOutAction.execute(timelineContext); + expect( + mockState.timeline.timelineById[TimelineId.active].filterManager?.addFilters + ).toHaveBeenCalled(); + expect(mockFilterManager.addFilters).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts new file mode 100644 index 0000000000000..dfe5b6cf46330 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts @@ -0,0 +1,57 @@ +/* + * 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 { addFilterIn, addFilterOut, createFilterOutActionFactory } from '@kbn/cell-actions'; +import { fieldHasCellActions } from '../../utils'; +import type { SecurityAppStore } from '../../../common/store'; +import type { StartServices } from '../../../types'; +import { timelineSelectors } from '../../../timelines/store/timeline'; +import { TimelineId } from '../../../../common/types'; +import { isTimelineScope } from '../../../helpers'; +import type { SecurityCellAction } from '../../types'; +import { SecurityCellActionType } from '../../constants'; + +export const createFilterOutCellActionFactory = ({ + store, + services, +}: { + store: SecurityAppStore; + services: StartServices; +}) => { + const getTimelineById = timelineSelectors.getTimelineByIdSelector(); + + const { filterManager } = services.data.query; + const genericFilterOutActionFactory = createFilterOutActionFactory({ filterManager }); + + return genericFilterOutActionFactory.combine({ + type: SecurityCellActionType.FILTER, + isCompatible: async ({ field }) => fieldHasCellActions(field.name), + execute: async ({ field, metadata }) => { + // if negateFilters is true we have to perform the opposite operation, we can just execute filterIn with the same params + const addFilter = metadata?.negateFilters === true ? addFilterIn : addFilterOut; + + if (metadata?.scopeId && isTimelineScope(metadata.scopeId)) { + const timelineFilterManager = getTimelineById( + store.getState(), + TimelineId.active + )?.filterManager; + + addFilter({ + filterManager: timelineFilterManager, + fieldName: field.name, + value: field.value, + }); + } else { + addFilter({ + filterManager, + fieldName: field.name, + value: field.value, + }); + } + }, + }); +}; diff --git a/x-pack/plugins/security_solution/public/actions/filter/default/filter_in.test.ts b/x-pack/plugins/security_solution/public/actions/filter/default/filter_in.test.ts deleted file mode 100644 index 5f09609f49a85..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/default/filter_in.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CellActionExecutionContext } from '@kbn/cell-actions'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { createFilterInAction } from './filter_in'; -import { createFilter } from '../helpers'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('../helpers'); - -const mockFilterManager = KibanaServices.get().data.query.filterManager; -const mockCreateFilter = createFilter as jest.Mock; - -describe('Default createFilterInAction', () => { - const filterInAction = createFilterInAction({ order: 1 }); - const context = { - field: { name: 'user.name', value: 'the value', type: 'text' }, - } as CellActionExecutionContext; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should return display name', () => { - expect(filterInAction.getDisplayName(context)).toEqual('Filter In'); - }); - - it('should return icon type', () => { - expect(filterInAction.getIconType(context)).toEqual('plusInCircle'); - }); - - describe('isCompatible', () => { - it('should return true if everything is okay', async () => { - expect(await filterInAction.isCompatible(context)).toEqual(true); - }); - }); - - describe('execute', () => { - it('should execute normally', async () => { - await filterInAction.execute(context); - expect(mockFilterManager.addFilters).toHaveBeenCalled(); - expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, false); - }); - - describe('should execute correctly when negateFilters is provided', () => { - it('if negateFilters is false, negate should be false (do not exclude)', async () => { - await filterInAction.execute({ - ...context, - metadata: { - negateFilters: false, - }, - }); - expect(mockFilterManager.addFilters).toHaveBeenCalled(); - expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, false); - }); - - it('if negateFilters is true, negate should be true (exclude)', async () => { - await filterInAction.execute({ - ...context, - metadata: { - negateFilters: true, - }, - }); - expect(mockFilterManager.addFilters).toHaveBeenCalled(); - expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, true); - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/default/filter_in.tsx b/x-pack/plugins/security_solution/public/actions/filter/default/filter_in.tsx deleted file mode 100644 index 06c0d7ee3ca4a..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/default/filter_in.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CellAction } from '@kbn/cell-actions'; -import { i18n } from '@kbn/i18n'; -import { createFilter } from '../helpers'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { fieldHasCellActions } from '../../utils'; - -export const FILTER_IN = i18n.translate('xpack.securitySolution.actions.filterIn', { - defaultMessage: 'Filter In', -}); -export const ACTION_ID = 'security_filterIn'; -const ICON = 'plusInCircle'; - -export const createFilterInAction = ({ order }: { order?: number }): CellAction => ({ - id: ACTION_ID, - type: ACTION_ID, - order, - getIconType: (): string => ICON, - getDisplayName: () => FILTER_IN, - getDisplayNameTooltip: () => FILTER_IN, - isCompatible: async ({ field }) => fieldHasCellActions(field.name), - execute: async ({ field, metadata }) => { - const services = KibanaServices.get(); - const filterManager = services.data.query.filterManager; - const negate = Boolean(metadata?.negateFilters); - - const makeFilter = (currentVal: string | string[] | null | undefined) => - currentVal?.length === 0 - ? createFilter(field.name, null) - : createFilter(field.name, currentVal, negate); - - if (filterManager != null) { - filterManager.addFilters(makeFilter(field.value)); - } - }, -}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/default/filter_out.test.ts b/x-pack/plugins/security_solution/public/actions/filter/default/filter_out.test.ts deleted file mode 100644 index 31a9fe6798f2b..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/default/filter_out.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CellActionExecutionContext } from '@kbn/cell-actions'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { createFilterOutAction } from './filter_out'; -import { createFilter } from '../helpers'; - -jest.mock('../../../common/lib/kibana'); -jest.mock('../helpers'); - -const mockFilterManager = KibanaServices.get().data.query.filterManager; -const mockCreateFilter = createFilter as jest.Mock; - -describe('Default createFilterOutAction', () => { - const filterInAction = createFilterOutAction({ order: 1 }); - const context = { - field: { name: 'user.name', value: 'the value', type: 'text' }, - } as CellActionExecutionContext; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should return display name', () => { - expect(filterInAction.getDisplayName(context)).toEqual('Filter Out'); - }); - - it('should return icon type', () => { - expect(filterInAction.getIconType(context)).toEqual('minusInCircle'); - }); - - describe('isCompatible', () => { - it('should return true if everything is okay', async () => { - expect(await filterInAction.isCompatible(context)).toEqual(true); - }); - }); - - describe('execute', () => { - it('should execute normally', async () => { - await filterInAction.execute(context); - expect(mockFilterManager.addFilters).toHaveBeenCalled(); - expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, true); - }); - - describe('should execute correctly when negateFilters is provided', () => { - it('if negateFilters is false, negate should be true', async () => { - await filterInAction.execute({ - ...context, - metadata: { - negateFilters: false, - }, - }); - expect(mockFilterManager.addFilters).toHaveBeenCalled(); - expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, true); - }); - - it('if negateFilters is true, negate should be false ', async () => { - await filterInAction.execute({ - ...context, - metadata: { - negateFilters: true, - }, - }); - expect(mockFilterManager.addFilters).toHaveBeenCalled(); - expect(mockCreateFilter).toBeCalledWith(context.field.name, context.field.value, false); - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/default/filter_out.tsx b/x-pack/plugins/security_solution/public/actions/filter/default/filter_out.tsx deleted file mode 100644 index 19fb6f323d745..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/default/filter_out.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { CellAction } from '@kbn/cell-actions'; -import { createFilter } from '../helpers'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { fieldHasCellActions } from '../../utils'; - -export const FILTER_OUT = i18n.translate('xpack.securitySolution.actions.filterOut', { - defaultMessage: 'Filter Out', -}); -export const ACTION_ID = 'security_filterOut'; -const ICON = 'minusInCircle'; - -export const createFilterOutAction = ({ order }: { order?: number }): CellAction => ({ - id: ACTION_ID, - type: ACTION_ID, - order, - getIconType: (): string => ICON, - getDisplayName: () => FILTER_OUT, - getDisplayNameTooltip: () => FILTER_OUT, - isCompatible: async ({ field }) => fieldHasCellActions(field.name), - execute: async ({ field, metadata }) => { - const services = KibanaServices.get(); - const filterManager = services.data.query.filterManager; - const negate = !metadata?.negateFilters; - - const makeFilter = (currentVal: string | string[] | null | undefined) => - currentVal == null || currentVal?.length === 0 - ? createFilter(field.name, null, false) - : createFilter(field.name, currentVal, negate); - - if (filterManager != null) { - filterManager.addFilters(makeFilter(field.value)); - } - }, -}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/helpers.ts b/x-pack/plugins/security_solution/public/actions/filter/helpers.ts deleted file mode 100644 index ffe89bf3fd718..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/helpers.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type { Filter } from '@kbn/es-query'; - -export const createFilter = ( - key: string, - value: string[] | string | null | undefined, - negate: boolean = false -): Filter => { - const queryValue = value != null ? (Array.isArray(value) ? value[0] : value) : null; - return queryValue != null - ? { - meta: { - alias: null, - negate, - disabled: false, - type: 'phrase', - key, - value: queryValue, - params: { - query: queryValue, - }, - }, - query: { - match: { - [key]: { - query: queryValue, - type: 'phrase', - }, - }, - }, - } - : ({ - exists: { - field: key, - }, - meta: { - alias: null, - disabled: false, - key, - negate: value === undefined, - type: 'exists', - value: 'exists', - }, - } as Filter); -}; diff --git a/x-pack/plugins/security_solution/public/actions/filter/index.ts b/x-pack/plugins/security_solution/public/actions/filter/index.ts index c86bdf801d6d3..a6e782d261db7 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/index.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/index.ts @@ -5,8 +5,5 @@ * 2.0. */ -export { createFilterInAction as createDefaultFilterInAction } from './default/filter_in'; -export { createFilterOutAction as createDefaultFilterOutAction } from './default/filter_out'; - -export { createFilterInAction as createTimelineFilterInAction } from './timeline/filter_in'; -export { createFilterOutAction as createTimelineFilterOutAction } from './timeline/filter_out'; +export { createFilterInCellActionFactory } from './cell_action/filter_in'; +export { createFilterOutCellActionFactory } from './cell_action/filter_out'; diff --git a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.test.ts b/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.test.ts deleted file mode 100644 index 20964ab5328b6..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CellActionExecutionContext } from '@kbn/cell-actions'; -import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; -import { TimelineId } from '../../../../common/types'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, -} from '../../../common/mock'; -import { createStore } from '../../../common/store'; -import { createFilterInAction } from './filter_in'; -import { Subject } from 'rxjs'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { APP_UI_ID } from '../../../../common/constants'; - -jest.mock('../../../common/lib/kibana'); - -const currentAppId$ = new Subject(); -KibanaServices.get().application.currentAppId$ = currentAppId$.asObservable(); - -const mockState = { - ...mockGlobalState, - timeline: { - ...mockGlobalState.timeline, - timelineById: { - ...mockGlobalState.timeline.timelineById, - [TimelineId.active]: { - ...mockGlobalState.timeline.timelineById[TimelineId.active], - filterManager: createFilterManagerMock(), - }, - }, - }, -}; - -const { storage } = createSecuritySolutionStorageMock(); -const mockStore = createStore(mockState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - -describe('Timeline createFilterInAction', () => { - const filterInAction = createFilterInAction({ store: mockStore, order: 1 }); - const context = { - field: { name: 'user.name', value: 'the value', type: 'text' }, - } as CellActionExecutionContext; - - beforeEach(() => { - jest.clearAllMocks(); - currentAppId$.next(APP_UI_ID); - }); - - it('should return display name', () => { - expect(filterInAction.getDisplayName(context)).toEqual('Filter In'); - }); - - it('should return icon type', () => { - expect(filterInAction.getIconType(context)).toEqual('plusInCircle'); - }); - - describe('isCompatible', () => { - it('should return true if everything is okay', async () => { - expect(await filterInAction.isCompatible(context)).toEqual(true); - }); - - it('should return false if not in Security', async () => { - currentAppId$.next('not security'); - expect(await filterInAction.isCompatible(context)).toEqual(false); - }); - }); - - describe('execute', () => { - it('should execute normally', async () => { - await filterInAction.execute(context); - expect( - mockState.timeline.timelineById[TimelineId.active].filterManager?.addFilters - ).toHaveBeenCalled(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.tsx b/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.tsx deleted file mode 100644 index 650c4f8b8b9d3..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_in.tsx +++ /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 { i18n } from '@kbn/i18n'; -import type { CellAction } from '@kbn/cell-actions'; -import { createFilter } from '../helpers'; -import type { SecurityAppStore } from '../../../common/store'; -import { timelineSelectors } from '../../../timelines/store/timeline'; -import { fieldHasCellActions, isInSecurityApp } from '../../utils'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { TimelineId } from '../../../../common/types'; - -export const FILTER_IN = i18n.translate('xpack.securitySolution.actions.filterIn', { - defaultMessage: 'Filter In', -}); -const ID = 'security_timeline_filterIn'; -const ICON = 'plusInCircle'; - -export const createFilterInAction = ({ - store, - order, -}: { - store: SecurityAppStore; - order?: number; -}): CellAction => { - const { application: applicationService } = KibanaServices.get(); - let currentAppId: string | undefined; - applicationService.currentAppId$.subscribe((appId) => { - currentAppId = appId; - }); - - return { - id: ID, - type: ID, - order, - getIconType: (): string => ICON, - getDisplayName: () => FILTER_IN, - getDisplayNameTooltip: () => FILTER_IN, - isCompatible: async ({ field }) => - isInSecurityApp(currentAppId) && fieldHasCellActions(field.name), - execute: async ({ field }) => { - const makeFilter = (currentVal?: string[] | string | null) => - currentVal?.length === 0 - ? createFilter(field.name, null) - : createFilter(field.name, currentVal); - - const state = store.getState(); - const getTimeline = timelineSelectors.getTimelineByIdSelector(); - const filterManager = getTimeline(state, TimelineId.active)?.filterManager; - - if (filterManager != null) { - filterManager.addFilters(makeFilter(field.value)); - } - }, - }; -}; diff --git a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.test.ts b/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.test.ts deleted file mode 100644 index e13d68c68cf68..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CellActionExecutionContext } from '@kbn/cell-actions'; -import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; -import { TimelineId } from '../../../../common/types'; -import { - createSecuritySolutionStorageMock, - kibanaObservable, - mockGlobalState, - SUB_PLUGINS_REDUCER, -} from '../../../common/mock'; -import { createStore } from '../../../common/store'; -import { Subject } from 'rxjs'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { APP_UI_ID } from '../../../../common/constants'; -import { createFilterOutAction } from './filter_out'; - -jest.mock('../../../common/lib/kibana'); - -const currentAppId$ = new Subject(); -KibanaServices.get().application.currentAppId$ = currentAppId$.asObservable(); - -const mockState = { - ...mockGlobalState, - timeline: { - ...mockGlobalState.timeline, - timelineById: { - ...mockGlobalState.timeline.timelineById, - [TimelineId.active]: { - ...mockGlobalState.timeline.timelineById[TimelineId.active], - filterManager: createFilterManagerMock(), - }, - }, - }, -}; - -const { storage } = createSecuritySolutionStorageMock(); -const mockStore = createStore(mockState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); - -describe('Timeline createFilterOutAction', () => { - const filterOutAction = createFilterOutAction({ store: mockStore, order: 1 }); - const context = { - field: { name: 'user.name', value: 'the value', type: 'text' }, - } as CellActionExecutionContext; - - beforeEach(() => { - jest.clearAllMocks(); - currentAppId$.next(APP_UI_ID); - }); - - it('should return display name', () => { - expect(filterOutAction.getDisplayName(context)).toEqual('Filter Out'); - }); - - it('should return icon type', () => { - expect(filterOutAction.getIconType(context)).toEqual('minusInCircle'); - }); - - describe('isCompatible', () => { - it('should return true if everything is okay', async () => { - expect(await filterOutAction.isCompatible(context)).toEqual(true); - }); - - it('should return false if not in Security', async () => { - currentAppId$.next('not security'); - expect(await filterOutAction.isCompatible(context)).toEqual(false); - }); - }); - - describe('execute', () => { - it('should execute normally', async () => { - await filterOutAction.execute(context); - expect( - mockState.timeline.timelineById[TimelineId.active].filterManager?.addFilters - ).toHaveBeenCalled(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.tsx b/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.tsx deleted file mode 100644 index e6c342bc861b0..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/filter/timeline/filter_out.tsx +++ /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 { i18n } from '@kbn/i18n'; -import type { CellAction } from '@kbn/cell-actions'; -import { createFilter } from '../helpers'; -import { KibanaServices } from '../../../common/lib/kibana'; -import { fieldHasCellActions, isInSecurityApp } from '../../utils'; -import type { SecurityAppStore } from '../../../common/store'; -import { timelineSelectors } from '../../../timelines/store/timeline'; -import { TimelineId } from '../../../../common/types'; - -export const FILTER_OUT = i18n.translate('xpack.securitySolution.actions.filterOut', { - defaultMessage: 'Filter Out', -}); -const ID = 'security_timeline_filterOut'; -const ICON = 'minusInCircle'; - -export const createFilterOutAction = ({ - store, - order, -}: { - store: SecurityAppStore; - order?: number; -}): CellAction => { - const { application: applicationService } = KibanaServices.get(); - let currentAppId: string | undefined; - applicationService.currentAppId$.subscribe((appId) => { - currentAppId = appId; - }); - - return { - id: ID, - type: ID, - order, - getIconType: (): string => ICON, - getDisplayName: () => FILTER_OUT, - getDisplayNameTooltip: () => FILTER_OUT, - isCompatible: async ({ field }) => - isInSecurityApp(currentAppId) && fieldHasCellActions(field.name), - execute: async ({ field }) => { - const makeFilter = (currentVal?: string[] | string | null) => - currentVal == null || currentVal?.length === 0 - ? createFilter(field.name, null, false) - : createFilter(field.name, currentVal, true); - - const state = store.getState(); - const getTimeline = timelineSelectors.getTimelineByIdSelector(); - const filterManager = getTimeline(state, TimelineId.active)?.filterManager; - - if (filterManager != null) { - filterManager.addFilters(makeFilter(field.value)); - } - }, - }; -}; diff --git a/x-pack/plugins/security_solution/public/actions/register.ts b/x-pack/plugins/security_solution/public/actions/register.ts index e113a242d6aa8..0fafb561d45a1 100644 --- a/x-pack/plugins/security_solution/public/actions/register.ts +++ b/x-pack/plugins/security_solution/public/actions/register.ts @@ -10,130 +10,84 @@ import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type * as H from 'history'; import type { SecurityAppStore } from '../common/store/types'; import type { StartPlugins, StartServices } from '../types'; +import { createFilterInCellActionFactory, createFilterOutCellActionFactory } from './filter'; import { - createLensCopyToClipboardAction, - createDefaultCopyToClipboardAction, -} from './copy_to_clipboard'; -import { - createDefaultFilterInAction, - createDefaultFilterOutAction, - createTimelineFilterInAction, - createTimelineFilterOutAction, -} from './filter'; -import { createLensAddToTimelineAction, createDefaultAddToTimelineAction } from './add_to_timeline'; -import { createDefaultShowTopNAction } from './show_top_n'; + createAddToTimelineLensAction, + createAddToTimelineCellActionFactory, +} from './add_to_timeline'; +import { createShowTopNCellActionFactory } from './show_top_n'; import { - CELL_ACTIONS_DEFAULT_TRIGGER, - CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, - CELL_ACTIONS_TIMELINE_TRIGGER, -} from '../../common/constants'; -import { createDefaultToggleColumnAction } from './toggle_column'; + createCopyToClipboardLensAction, + createCopyToClipboardCellActionFactory, +} from './copy_to_clipboard'; +import { createToggleColumnCellActionFactory } from './toggle_column'; +import { SecurityCellActionsTrigger } from './constants'; +import type { SecurityCellActionName, SecurityCellActions } from './types'; export const registerUIActions = ( - plugins: StartPlugins, + { uiActions }: StartPlugins, store: SecurityAppStore, history: H.History, services: StartServices ) => { - registerLensActions(plugins.uiActions, store); - registerDefaultActions(plugins.uiActions, store, history, services); - registerTimelineActions(plugins.uiActions, store, history, services); - registerTableFlyoutActions(plugins.uiActions, store, history, services); + registerLensActions(uiActions, store); + registerCellActions(uiActions, store, history, services); }; const registerLensActions = (uiActions: UiActionsStart, store: SecurityAppStore) => { - const addToTimelineAction = createLensAddToTimelineAction({ store, order: 1 }); + const addToTimelineAction = createAddToTimelineLensAction({ store, order: 1 }); uiActions.addTriggerAction(CELL_VALUE_TRIGGER, addToTimelineAction); - const copyToClipboardAction = createLensCopyToClipboardAction({ order: 2 }); + const copyToClipboardAction = createCopyToClipboardLensAction({ order: 2 }); uiActions.addTriggerAction(CELL_VALUE_TRIGGER, copyToClipboardAction); }; -const registerDefaultActions = ( - uiActions: UiActionsStart, - store: SecurityAppStore, - history: H.History, - services: StartServices -) => { - const filterInAction = createDefaultFilterInAction({ - order: 10, - }); - const filterOutAction = createDefaultFilterOutAction({ - order: 20, - }); - const addToTimeline = createDefaultAddToTimelineAction({ store, order: 30 }); - const showTopNAction = createDefaultShowTopNAction({ store, history, services, order: 40 }); - const copyAction = createDefaultCopyToClipboardAction({ order: 50 }); - - uiActions.registerTrigger({ - id: CELL_ACTIONS_DEFAULT_TRIGGER, - }); - - uiActions.addTriggerAction(CELL_ACTIONS_DEFAULT_TRIGGER, copyAction); - uiActions.addTriggerAction(CELL_ACTIONS_DEFAULT_TRIGGER, filterInAction); - uiActions.addTriggerAction(CELL_ACTIONS_DEFAULT_TRIGGER, filterOutAction); - uiActions.addTriggerAction(CELL_ACTIONS_DEFAULT_TRIGGER, showTopNAction); - uiActions.addTriggerAction(CELL_ACTIONS_DEFAULT_TRIGGER, addToTimeline); -}; - -const registerTimelineActions = ( +const registerCellActions = ( uiActions: UiActionsStart, store: SecurityAppStore, history: H.History, services: StartServices ) => { - const filterInAction = createTimelineFilterInAction({ - store, - order: 10, - }); - const filterOutAction = createTimelineFilterOutAction({ - store, - order: 20, - }); - const addToTimeline = createDefaultAddToTimelineAction({ store, order: 30 }); - const showTopNAction = createDefaultShowTopNAction({ store, history, services, order: 40 }); - const copyAction = createDefaultCopyToClipboardAction({ order: 50 }); + const cellActions: SecurityCellActions = { + filterIn: createFilterInCellActionFactory({ store, services }), + filterOut: createFilterOutCellActionFactory({ store, services }), + addToTimeline: createAddToTimelineCellActionFactory({ store, services }), + showTopN: createShowTopNCellActionFactory({ store, history, services }), + copyToClipboard: createCopyToClipboardCellActionFactory({ services }), + toggleColumn: createToggleColumnCellActionFactory({ store }), + }; - uiActions.registerTrigger({ - id: CELL_ACTIONS_TIMELINE_TRIGGER, - }); + registerCellActionsTrigger(uiActions, SecurityCellActionsTrigger.DEFAULT, cellActions, [ + 'filterIn', + 'filterOut', + 'addToTimeline', + 'showTopN', + 'copyToClipboard', + ]); - uiActions.addTriggerAction(CELL_ACTIONS_TIMELINE_TRIGGER, copyAction); - uiActions.addTriggerAction(CELL_ACTIONS_TIMELINE_TRIGGER, filterInAction); - uiActions.addTriggerAction(CELL_ACTIONS_TIMELINE_TRIGGER, filterOutAction); - uiActions.addTriggerAction(CELL_ACTIONS_TIMELINE_TRIGGER, showTopNAction); - uiActions.addTriggerAction(CELL_ACTIONS_TIMELINE_TRIGGER, addToTimeline); + registerCellActionsTrigger(uiActions, SecurityCellActionsTrigger.DETAILS_FLYOUT, cellActions, [ + 'filterIn', + 'filterOut', + 'addToTimeline', + 'toggleColumn', + 'showTopN', + 'copyToClipboard', + ]); }; -/** - * This actions show up in when a details flyout is open from a table field. - */ -const registerTableFlyoutActions = ( +const registerCellActionsTrigger = ( uiActions: UiActionsStart, - store: SecurityAppStore, - history: H.History, - services: StartServices + triggerId: SecurityCellActionsTrigger, + cellActions: SecurityCellActions, + actionsOrder: SecurityCellActionName[] ) => { - const filterInAction = createDefaultFilterInAction({ - order: 10, - }); - const filterOutAction = createDefaultFilterOutAction({ - order: 20, - }); - - const addToTimeline = createDefaultAddToTimelineAction({ store, order: 30 }); - const toggleAction = createDefaultToggleColumnAction({ store, order: 35 }); - const showTopNAction = createDefaultShowTopNAction({ store, history, services, order: 40 }); - const copyAction = createDefaultCopyToClipboardAction({ order: 50 }); + uiActions.registerTrigger({ id: triggerId }); - uiActions.registerTrigger({ - id: CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, + actionsOrder.forEach((actionName, order) => { + const actionFactory = cellActions[actionName]; + uiActions.addTriggerAction( + triggerId, + actionFactory({ id: `${triggerId}-${actionName}`, order }) + ); }); - - uiActions.addTriggerAction(CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, copyAction); - uiActions.addTriggerAction(CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, filterInAction); - uiActions.addTriggerAction(CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, filterOutAction); - uiActions.addTriggerAction(CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, showTopNAction); - uiActions.addTriggerAction(CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, addToTimeline); - uiActions.addTriggerAction(CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER, toggleAction); }; diff --git a/x-pack/plugins/security_solution/public/actions/show_top_n/default/show_top_n.test.tsx b/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.test.tsx similarity index 77% rename from x-pack/plugins/security_solution/public/actions/show_top_n/default/show_top_n.test.tsx rename to x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.test.tsx index a2e047bc5e415..58754bdf8bbbd 100644 --- a/x-pack/plugins/security_solution/public/actions/show_top_n/default/show_top_n.test.tsx +++ b/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.test.tsx @@ -6,8 +6,6 @@ */ import type { CellActionExecutionContext } from '@kbn/cell-actions'; -import { Subject } from 'rxjs'; -import { APP_UI_ID } from '../../../../common/constants'; import { createSecuritySolutionStorageMock, kibanaObservable, @@ -16,7 +14,7 @@ import { } from '../../../common/mock'; import { mockHistory } from '../../../common/mock/router'; import { createStore } from '../../../common/store'; -import { createShowTopNAction } from './show_top_n'; +import { createShowTopNCellActionFactory } from './show_top_n'; import React from 'react'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; @@ -26,41 +24,32 @@ jest.mock('../show_top_n_component', () => ({ TopNAction: () => {'TEST COMPONENT'}, })); -const currentAppId$ = new Subject(); -const startServices = createStartServicesMock(); - -const mockServices = { - ...startServices, - application: { - ...startServices.application, - currentAppId$: currentAppId$.asObservable(), - }, -}; - +const mockServices = createStartServicesMock(); const { storage } = createSecuritySolutionStorageMock(); const mockStore = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); const element = document.createElement('div'); document.body.appendChild(element); -describe('createShowTopNAction', () => { - const showTopNAction = createShowTopNAction({ +describe('createShowTopNCellActionFactory', () => { + const showTopNActionFactory = createShowTopNCellActionFactory({ store: mockStore, history: mockHistory, - order: 1, services: mockServices, }); + const showTopNAction = showTopNActionFactory({ id: 'testAction' }); + const context = { field: { name: 'user.name', value: 'the-value', type: 'keyword', aggregatable: true }, trigger: { id: 'trigger' }, nodeRef: { current: element, }, + metadata: undefined, } as CellActionExecutionContext; beforeEach(() => { jest.clearAllMocks(); - currentAppId$.next(APP_UI_ID); }); it('should return display name', () => { @@ -76,13 +65,7 @@ describe('createShowTopNAction', () => { expect(await showTopNAction.isCompatible(context)).toEqual(true); }); - it('should return false if not in Security', async () => { - currentAppId$.next('not security'); - expect(await showTopNAction.isCompatible(context)).toEqual(false); - }); - it('should return false if field type does not support aggregations', async () => { - currentAppId$.next('not security'); expect( await showTopNAction.isCompatible({ ...context, field: { ...context.field, type: 'text' } }) ).toEqual(false); diff --git a/x-pack/plugins/security_solution/public/actions/show_top_n/default/show_top_n.tsx b/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.tsx similarity index 73% rename from x-pack/plugins/security_solution/public/actions/show_top_n/default/show_top_n.tsx rename to x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.tsx index 215033409a46f..557210286678e 100644 --- a/x-pack/plugins/security_solution/public/actions/show_top_n/default/show_top_n.tsx +++ b/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.tsx @@ -4,23 +4,22 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { i18n } from '@kbn/i18n'; -import type { CellAction, CellActionExecutionContext } from '@kbn/cell-actions'; -import ReactDOM, { unmountComponentAtNode } from 'react-dom'; import React from 'react'; - +import ReactDOM, { unmountComponentAtNode } from 'react-dom'; import type * as H from 'history'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { Provider } from 'react-redux'; import { Router } from 'react-router-dom'; - +import { i18n } from '@kbn/i18n'; +import { createCellActionFactory, type CellActionTemplate } from '@kbn/cell-actions'; +import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { KibanaContextProvider } from '../../../common/lib/kibana'; import { APP_NAME, DEFAULT_DARK_MODE } from '../../../../common/constants'; import type { SecurityAppStore } from '../../../common/store'; -import { fieldHasCellActions, isInSecurityApp } from '../../utils'; +import { fieldHasCellActions } from '../../utils'; import { TopNAction } from '../show_top_n_component'; import type { StartServices } from '../../../types'; +import type { SecurityCellAction } from '../../types'; +import { SecurityCellActionType } from '../../constants'; const SHOW_TOP = (fieldName: string) => i18n.translate('xpack.securitySolution.actions.showTopTooltip', { @@ -28,42 +27,24 @@ const SHOW_TOP = (fieldName: string) => defaultMessage: `Show top {fieldName}`, }); -export const ACTION_ID = 'security_showTopN'; const ICON = 'visBarVertical'; const UNSUPPORTED_FIELD_TYPES = ['date', 'text']; -export interface ShowTopNActionContext extends CellActionExecutionContext { - metadata?: { - scopeId?: string; - }; -} - -export const createShowTopNAction = ({ - store, - history, - services, - order, -}: { - store: SecurityAppStore; - history: H.History; - services: StartServices; - order?: number; -}): CellAction => { - let currentAppId: string | undefined; - - services.application.currentAppId$.subscribe((appId) => { - currentAppId = appId; - }); - - return { - id: ACTION_ID, - type: ACTION_ID, - order, - getIconType: (): string => ICON, +export const createShowTopNCellActionFactory = createCellActionFactory( + ({ + store, + history, + services, + }: { + store: SecurityAppStore; + history: H.History; + services: StartServices; + }): CellActionTemplate => ({ + type: SecurityCellActionType.SHOW_TOP_N, + getIconType: () => ICON, getDisplayName: ({ field }) => SHOW_TOP(field.name), getDisplayNameTooltip: ({ field }) => SHOW_TOP(field.name), isCompatible: async ({ field }) => - isInSecurityApp(currentAppId) && fieldHasCellActions(field.name) && !UNSUPPORTED_FIELD_TYPES.includes(field.type) && !!field.aggregatable, @@ -97,5 +78,5 @@ export const createShowTopNAction = ({ ReactDOM.render(element, node); }, - }; -}; + }) +); diff --git a/x-pack/plugins/security_solution/public/actions/show_top_n/index.ts b/x-pack/plugins/security_solution/public/actions/show_top_n/index.ts index 42c14331aa06a..df848ce8e474c 100644 --- a/x-pack/plugins/security_solution/public/actions/show_top_n/index.ts +++ b/x-pack/plugins/security_solution/public/actions/show_top_n/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { createShowTopNAction as createDefaultShowTopNAction } from './default/show_top_n'; +export { createShowTopNCellActionFactory } from './cell_action/show_top_n'; diff --git a/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.test.tsx b/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.test.tsx index 1737a65596a78..73c56b983b377 100644 --- a/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.test.tsx +++ b/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.test.tsx @@ -35,6 +35,7 @@ const context = { nodeRef: { current: element, }, + metadata: undefined, } as CellActionExecutionContext; describe('TopNAction', () => { diff --git a/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.tsx b/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.tsx index 7d426b03ec605..bc231ec106975 100644 --- a/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.tsx +++ b/x-pack/plugins/security_solution/public/actions/show_top_n/show_top_n_component.tsx @@ -14,7 +14,7 @@ import { StatefulTopN } from '../../common/components/top_n'; import { useGetUserCasesPermissions } from '../../common/lib/kibana'; import { APP_ID } from '../../../common/constants'; import { getScopeFromPath, useSourcererDataView } from '../../common/containers/sourcerer'; -import type { ShowTopNActionContext } from './default/show_top_n'; +import type { SecurityCellActionExecutionContext } from '../types'; export const TopNAction = ({ onClose, @@ -22,7 +22,7 @@ export const TopNAction = ({ casesService, }: { onClose: () => void; - context: ShowTopNActionContext; + context: SecurityCellActionExecutionContext; casesService: CasesUiStart; }) => { const { pathname } = useLocation(); diff --git a/x-pack/plugins/security_solution/public/actions/toggle_column/default/toggle_column.test.ts b/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.test.ts similarity index 84% rename from x-pack/plugins/security_solution/public/actions/toggle_column/default/toggle_column.test.ts rename to x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.test.ts index a62e0cedff19a..fffd6a21a9b42 100644 --- a/x-pack/plugins/security_solution/public/actions/toggle_column/default/toggle_column.test.ts +++ b/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.test.ts @@ -6,19 +6,13 @@ */ import type { SecurityAppStore } from '../../../common/store/types'; -import { KibanaServices } from '../../../common/lib/kibana'; import { TableId } from '../../../../common/types'; -import { createToggleColumnAction } from './toggle_column'; +import { createToggleColumnCellActionFactory } from './toggle_column'; import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { mockGlobalState } from '../../../common/mock'; import { dataTableActions } from '../../../common/store/data_table'; -jest.mock('../../../common/lib/kibana'); - -const mockWarningToast = jest.fn(); -KibanaServices.get().notifications.toasts.addWarning = mockWarningToast; - const mockDispatch = jest.fn(); const mockGetState = jest.fn().mockReturnValue(mockGlobalState); const store = { @@ -35,8 +29,9 @@ const context = { }, } as unknown as CellActionExecutionContext; -describe('Default createToggleColumnAction', () => { - const toggleColumnAction = createToggleColumnAction({ store, order: 1 }); +describe('createToggleColumnCellActionFactory', () => { + const toggleColumnActionFactory = createToggleColumnCellActionFactory({ store }); + const toggleColumnAction = toggleColumnActionFactory({ id: 'testAction' }); beforeEach(() => { jest.clearAllMocks(); @@ -80,7 +75,6 @@ describe('Default createToggleColumnAction', () => { id: TableId.test, }) ); - expect(mockWarningToast).not.toHaveBeenCalled(); }); it('should add column', async () => { @@ -97,7 +91,6 @@ describe('Default createToggleColumnAction', () => { index: 1, }) ); - expect(mockWarningToast).not.toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts b/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts new file mode 100644 index 0000000000000..feb555851ba8d --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts @@ -0,0 +1,84 @@ +/* + * 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 { createCellActionFactory, type CellActionTemplate } from '@kbn/cell-actions'; +import { fieldHasCellActions } from '../../utils'; +import type { SecurityAppStore } from '../../../common/store'; +import { getScopedActions, isInTableScope, isTimelineScope } from '../../../helpers'; +import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { defaultColumnHeaderType, tableDefaults } from '../../../common/store/data_table/defaults'; +import { timelineSelectors } from '../../../timelines/store/timeline'; +import { dataTableSelectors } from '../../../common/store/data_table'; +import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; +import type { SecurityCellAction } from '../../types'; +import { SecurityCellActionType } from '../../constants'; + +const ICON = 'listAdd'; +const COLUMN_TOGGLE = i18n.translate('xpack.securitySolution.actions.toggleColumnToggle.label', { + defaultMessage: 'Toggle column in table', +}); +const NESTED_COLUMN = (field: string) => + i18n.translate('xpack.securitySolution.actions.toggleColumnToggle.nestedLabel', { + values: { field }, + defaultMessage: + 'The {field} field is an object, and is broken down into nested fields which can be added as columns', + }); + +export const createToggleColumnCellActionFactory = createCellActionFactory( + ({ store }: { store: SecurityAppStore }): CellActionTemplate => ({ + type: SecurityCellActionType.TOGGLE_COLUMN, + getIconType: () => ICON, + getDisplayName: () => COLUMN_TOGGLE, + getDisplayNameTooltip: ({ field, metadata }) => + metadata?.isObjectArray ? NESTED_COLUMN(field.name) : COLUMN_TOGGLE, + isCompatible: async ({ field, metadata }) => { + return ( + fieldHasCellActions(field.name) && + !!metadata?.scopeId && + (isTimelineScope(metadata.scopeId) || isInTableScope(metadata.scopeId)) + ); + }, + execute: async ({ metadata, field }) => { + const scopeId = metadata?.scopeId; + if (!scopeId) return; + + const scopedActions = getScopedActions(scopeId); + if (!scopedActions) { + return; + } + + const selector = isTimelineScope(scopeId) + ? timelineSelectors.getTimelineByIdSelector() + : dataTableSelectors.getTableByIdSelector(); + + const defaults = isTimelineScope(scopeId) ? timelineDefaults : tableDefaults; + const { columns } = selector(store.getState(), scopeId) ?? defaults; + + if (columns.some((c) => c.id === field.name)) { + store.dispatch( + scopedActions.removeColumn({ + columnId: field.name, + id: scopeId, + }) + ); + } else { + store.dispatch( + scopedActions.upsertColumn({ + column: { + columnHeaderType: defaultColumnHeaderType, + id: field.name, + initialWidth: DEFAULT_COLUMN_MIN_WIDTH, + }, + id: scopeId, + index: 1, + }) + ); + } + }, + }) +); diff --git a/x-pack/plugins/security_solution/public/actions/toggle_column/default/toggle_column.ts b/x-pack/plugins/security_solution/public/actions/toggle_column/default/toggle_column.ts deleted file mode 100644 index 5ebdad769f1d9..0000000000000 --- a/x-pack/plugins/security_solution/public/actions/toggle_column/default/toggle_column.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { CellAction, CellActionExecutionContext } from '@kbn/cell-actions'; - -import { fieldHasCellActions } from '../../utils'; -import type { SecurityAppStore } from '../../../common/store'; -import { getScopedActions, isInTableScope, isTimelineScope } from '../../../helpers'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; -import { defaultColumnHeaderType, tableDefaults } from '../../../common/store/data_table/defaults'; -import { timelineSelectors } from '../../../timelines/store/timeline'; -import { dataTableSelectors } from '../../../common/store/data_table'; -import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; - -export const ACTION_ID = 'security_toggleColumn'; -const ICON = 'listAdd'; - -export interface ShowTopNActionContext extends CellActionExecutionContext { - metadata?: { - scopeId?: string; - isObjectArray?: boolean; - }; -} - -export const COLUMN_TOGGLE = i18n.translate( - 'xpack.securitySolution.actions.toggleColumnToggle.label', - { - defaultMessage: 'Toggle column in table', - } -); - -export const NESTED_COLUMN = (field: string) => - i18n.translate('xpack.securitySolution.actions.toggleColumnToggle.nestedLabel', { - values: { field }, - defaultMessage: - 'The {field} field is an object, and is broken down into nested fields which can be added as columns', - }); - -export const createToggleColumnAction = ({ - store, - order, -}: { - store: SecurityAppStore; - order?: number; -}): CellAction => ({ - id: ACTION_ID, - type: ACTION_ID, - order, - getIconType: (): string => ICON, - getDisplayName: () => COLUMN_TOGGLE, - getDisplayNameTooltip: ({ field, metadata }) => - metadata?.isObjectArray ? NESTED_COLUMN(field.name) : COLUMN_TOGGLE, - isCompatible: async ({ field, metadata }) => { - return ( - fieldHasCellActions(field.name) && - !!metadata?.scopeId && - (isTimelineScope(metadata?.scopeId) || isInTableScope(metadata?.scopeId)) - ); - }, - execute: async ({ metadata, field }) => { - const scopeId = metadata?.scopeId; - - if (!scopeId) return; - - const selector = isTimelineScope(scopeId) - ? timelineSelectors.getTimelineByIdSelector() - : dataTableSelectors.getTableByIdSelector(); - - const defaults = isTimelineScope(scopeId) ? timelineDefaults : tableDefaults; - const { columns } = selector(store.getState(), scopeId) ?? defaults; - - const scopedActions = getScopedActions(scopeId); - - if (!scopedActions) { - return; - } - - if (columns.some((c) => c.id === field.name)) { - store.dispatch( - scopedActions.removeColumn({ - columnId: field.name, - id: scopeId, - }) - ); - } else { - store.dispatch( - scopedActions.upsertColumn({ - column: { - columnHeaderType: defaultColumnHeaderType, - id: field.name, - initialWidth: DEFAULT_COLUMN_MIN_WIDTH, - }, - id: scopeId, - index: 1, - }) - ); - } - }, -}); diff --git a/x-pack/plugins/security_solution/public/actions/toggle_column/index.tsx b/x-pack/plugins/security_solution/public/actions/toggle_column/index.tsx index da04abfda3734..4e1fb2a03fc46 100644 --- a/x-pack/plugins/security_solution/public/actions/toggle_column/index.tsx +++ b/x-pack/plugins/security_solution/public/actions/toggle_column/index.tsx @@ -5,4 +5,4 @@ * 2.0. */ -export { createToggleColumnAction as createDefaultToggleColumnAction } from './default/toggle_column'; +export { createToggleColumnCellActionFactory } from './cell_action/toggle_column'; diff --git a/x-pack/plugins/security_solution/public/actions/types.ts b/x-pack/plugins/security_solution/public/actions/types.ts new file mode 100644 index 0000000000000..da6b2cc3919a9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/types.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CellAction, CellActionExecutionContext, CellActionFactory } from '@kbn/cell-actions'; + +export interface SecurityMetadata extends Record { + /** + * `metadata.scopeId` is used by some actions (e.g. filterIn/Out) to discriminate the Timeline + * and the DataTables scope (alerts, events, rules preview..) in the actions execution. + * It is required when cellActions are rendered inside the Timeline or dataTables, + * it can be omitted otherwise. + */ + scopeId?: string; + /** + * `metadata.isObjectArray` is used to display extended tooltip information + * for fields that have multiple values + */ + isObjectArray?: boolean; + /** + * `metadata.negateFilters` is used by some actions (e.g. filterIn/Out and addToTimeline) to negate + * the usual filtering behavior. This is used in special cases where the displayed value is computed + * and we need all the filtering actions to perform the opposite (negate) operation. + */ + negateFilters?: boolean; +} + +export interface SecurityCellActionExecutionContext extends CellActionExecutionContext { + metadata: SecurityMetadata | undefined; +} +export type SecurityCellAction = CellAction; + +// All security cell actions names +export type SecurityCellActionName = + | 'filterIn' + | 'filterOut' + | 'addToTimeline' + | 'showTopN' + | 'copyToClipboard' + | 'toggleColumn'; + +export type SecurityCellActions = Record; diff --git a/x-pack/plugins/security_solution/public/actions/utils.ts b/x-pack/plugins/security_solution/public/actions/utils.ts index 4544d180175e2..74b40334284cf 100644 --- a/x-pack/plugins/security_solution/public/actions/utils.ts +++ b/x-pack/plugins/security_solution/public/actions/utils.ts @@ -7,10 +7,9 @@ import type { IEmbeddable } from '@kbn/embeddable-plugin/public'; import { LENS_EMBEDDABLE_TYPE, type Embeddable as LensEmbeddable } from '@kbn/lens-plugin/public'; import type { Serializable } from '@kbn/utility-types'; - import { APP_UI_ID } from '../../common/constants'; -/** all cell actions are disabled for these fields */ +// All cell actions are disabled for these fields in Security const FIELDS_WITHOUT_CELL_ACTIONS = [ 'signal.rule.risk_score', 'kibana.alert.risk_score', diff --git a/x-pack/plugins/security_solution/public/common/components/cell_actions/index.ts b/x-pack/plugins/security_solution/public/common/components/cell_actions/index.ts new file mode 100644 index 0000000000000..23a11aa738443 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/cell_actions/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CellActions, useDataGridColumnsCellActions } from '@kbn/cell-actions'; +import type { + CellActionsProps, + UseDataGridColumnsCellActions, + UseDataGridColumnsCellActionsProps, +} from '@kbn/cell-actions'; +import type { SecurityMetadata } from '../../../actions/types'; +import { SecurityCellActionsTrigger, SecurityCellActionType } from '../../../actions/constants'; + +// bridge exports for convenience +export * from '@kbn/cell-actions'; +export { SecurityCellActionsTrigger, SecurityCellActionType }; + +export interface SecurityCellActionsProps extends CellActionsProps { + triggerId: string; // can not use SecurityCellActionsTrigger, React.FC Validation throws error for some reason + disabledActionTypes?: string[]; // can not use SecurityCellActionType[], React.FC Validation throws error for some reason + metadata?: SecurityMetadata; +} +export interface UseDataGridColumnsSecurityCellActionsProps + extends UseDataGridColumnsCellActionsProps { + triggerId: SecurityCellActionsTrigger; + disabledActionTypes?: SecurityCellActionType[]; + metadata?: SecurityMetadata; +} + +// same components with security cell actions types +export const SecurityCellActions: React.FC = CellActions; +export const useDataGridColumnsSecurityCellActions: UseDataGridColumnsCellActions = + useDataGridColumnsCellActions; diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/data_table/index.test.tsx index 527eb6e3d0ab9..86cf4eabeba1b 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/data_table/index.test.tsx @@ -18,7 +18,7 @@ import { mockBrowserFields } from '../../containers/source/mock'; import { getMappedNonEcsValue } from '../../../timelines/components/timeline/body/data_driven_columns'; import type { CellValueElementProps } from '../../../../common/types'; import { TableId } from '../../../../common/types'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../common/constants'; +import { SecurityCellActionsTrigger } from '../cell_actions'; const mockDispatch = jest.fn(); jest.mock('react-redux', () => ({ @@ -184,7 +184,7 @@ describe('DataTable', () => { wrapper.update(); expect(mockUseDataGridColumnsCellActions).toHaveBeenCalledWith({ - triggerId: CELL_ACTIONS_DEFAULT_TRIGGER, + triggerId: SecurityCellActionsTrigger.DEFAULT, fields: [ { name: '@timestamp', diff --git a/x-pack/plugins/security_solution/public/common/components/data_table/index.tsx b/x-pack/plugins/security_solution/public/common/components/data_table/index.tsx index bfddac06f64b5..a48bf61f4ac93 100644 --- a/x-pack/plugins/security_solution/public/common/components/data_table/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/data_table/index.tsx @@ -26,9 +26,10 @@ import type { EuiTheme } from '@kbn/kibana-react-plugin/common'; import type { FieldBrowserOptions } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; import { - useDataGridColumnsCellActions, - type UseDataGridColumnsCellActionsProps, -} from '@kbn/cell-actions'; + useDataGridColumnsSecurityCellActions, + SecurityCellActionsTrigger, + type UseDataGridColumnsSecurityCellActionsProps, +} from '../cell_actions'; import type { CellValueElementProps, ColumnHeaderOptions, @@ -49,7 +50,6 @@ import { getPageRowIndex } from './pagination'; import { UnitCount } from '../toolbar/unit'; import { useShallowEqualSelector } from '../../hooks/use_selector'; import { tableDefaults } from '../../store/data_table/defaults'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../common/constants'; const DATA_TABLE_ARIA_LABEL = i18n.translate('xpack.securitySolution.dataTable.ariaLabel', { defaultMessage: 'Alerts', @@ -303,8 +303,8 @@ export const DataTableComponent = React.memo( [dispatch, id] ); - const columnsCellActionsProps = useMemo((): UseDataGridColumnsCellActionsProps => { - const fields: UseDataGridColumnsCellActionsProps['fields'] = disableCellActions + const columnsCellActionsProps = useMemo(() => { + const fields = disableCellActions ? [] : columnHeaders.map((column) => ({ name: column.id, @@ -317,7 +317,7 @@ export const DataTableComponent = React.memo( })); return { - triggerId: CELL_ACTIONS_DEFAULT_TRIGGER, + triggerId: SecurityCellActionsTrigger.DEFAULT, fields, metadata: { scopeId: id, @@ -326,7 +326,7 @@ export const DataTableComponent = React.memo( }; }, [disableCellActions, columnHeaders, data, id]); - const columnsCellActions = useDataGridColumnsCellActions(columnsCellActionsProps); + const columnsCellActions = useDataGridColumnsSecurityCellActions(columnsCellActionsProps); const columnsWithCellActions: EuiDataGridColumn[] = useMemo( () => diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx index 7cf91c27d13b7..7f68eb73a2355 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx @@ -10,14 +10,13 @@ import { get } from 'lodash'; import memoizeOne from 'memoize-one'; import React from 'react'; import styled from 'styled-components'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActions, CellActionsMode, SecurityCellActionsTrigger } from '../cell_actions'; import type { BrowserFields } from '../../containers/source'; import * as i18n from './translations'; import type { EventFieldsData } from './types'; import type { BrowserField } from '../../../../common/search_strategy'; import { FieldValueCell } from './table/field_value_cell'; import { FieldNameCell } from './table/field_name_cell'; -import { CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER } from '../../../../common/constants'; const HoverActionsContainer = styled(EuiPanel)` align-items: center; @@ -74,14 +73,14 @@ export const getColumns = ({ ); return ( - = ({ {value && !isReadOnly && ( - theme.eui.euiSizeS}; @@ -88,14 +91,14 @@ export const OverviewCardWithActions: React.FC = ( {children} - = ({ values={values} /> {scopeId !== TimelineId.active && !isReadOnly && hoverActionsEnabled && ( - 0 ? values[0] : '', type: data.type, aggregatable: fieldFromBrowserField?.aggregatable, }} - triggerId={CELL_ACTIONS_DETAILS_FLYOUT_TRIGGER} + triggerId={SecurityCellActionsTrigger.DETAILS_FLYOUT} mode={CellActionsMode.INLINE} visibleCellActions={3} metadata={{ scopeId }} diff --git a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap index 3c834b31ddfaa..941078e41f917 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap @@ -11,7 +11,7 @@ exports[`entity_draggable renders correctly against snapshot 1`] = ` } } mode="hover" - triggerId="security-solution-default-cellActions" + triggerId="security-default-cellActions" visibleCellActions={5} > entity-name: "entity-value" diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx index 5b9f01628570d..00a8c0cdb3f39 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx @@ -6,8 +6,7 @@ */ import React from 'react'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../common/constants'; +import { SecurityCellActions, CellActionsMode, SecurityCellActionsTrigger } from '../cell_actions'; interface Props { entityName: string; @@ -16,19 +15,19 @@ interface Props { export const EntityComponent: React.FC = ({ entityName, entityValue }) => { return ( - {`${entityName}: "${entityValue}"`} - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap index c244dd622d8a2..08e1bbe2bfc80 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap @@ -11,7 +11,7 @@ exports[`draggable_score renders correctly against snapshot 1`] = ` } } mode="hover" - triggerId="security-solution-default-cellActions" + triggerId="security-default-cellActions" visibleCellActions={5} > 17 @@ -29,7 +29,7 @@ exports[`draggable_score renders correctly against snapshot when the index is no } } mode="hover" - triggerId="security-solution-default-cellActions" + triggerId="security-default-cellActions" visibleCellActions={5} > 17 diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx index 18596ec2c16b8..3ad058dd2ab33 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx @@ -6,11 +6,14 @@ */ import React from 'react'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../cell_actions'; import type { Anomaly } from '../types'; import { Spacer } from '../../page'; import { getScoreString } from './score_health'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; export const ScoreComponent = ({ index = 0, @@ -22,7 +25,7 @@ export const ScoreComponent = ({ const scoreString = getScoreString(score.severity); return ( - <> @@ -42,7 +45,7 @@ export const ScoreComponent = ({ )} {scoreString} - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx index c6fa5291e1e61..6705cc9b871fe 100644 --- a/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/tables/helpers.tsx @@ -8,11 +8,10 @@ import React, { useCallback, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiLink, EuiPopover, EuiToolTip, EuiText, EuiTextColor } from '@elastic/eui'; import styled from 'styled-components'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActions, CellActionsMode, SecurityCellActionsTrigger } from '../cell_actions'; import { escapeDataProviderId } from '../drag_and_drop/helpers'; import { defaultToEmptyTag, getEmptyTagValue } from '../empty_value'; import { MoreRowItems } from '../page'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../common/constants'; import { MoreContainer } from '../../../timelines/components/field_renderers/field_renderers'; const Subtext = styled.div` @@ -44,12 +43,12 @@ export const getRowItemsWithActions = ({ const visibleItems = values.slice(0, displayCount).map((value, index) => { const id = escapeDataProviderId(`${idPrefix}-${fieldName}-${value}-${index}`); return ( - <>{render ? render(value) : defaultToEmptyTag(value)} - + ); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx index 63bac25889831..f8df50e9f67d4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_by_type_panel/columns.tsx @@ -7,15 +7,18 @@ import React from 'react'; import { EuiHealth, EuiText } from '@elastic/eui'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; import type { EuiBasicTableColumn } from '@elastic/eui'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; import type { AlertsTypeData, AlertType } from './types'; import { DefaultDraggable } from '../../../../common/components/draggables'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { ALERTS_HEADERS_RULE_NAME } from '../../alerts_table/translations'; import { ALERT_TYPE_COLOR, ALERT_TYPE_LABEL } from './helpers'; import { COUNT_TABLE_TITLE } from '../alerts_count_panel/translations'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; import * as i18n from './translations'; export const getAlertsTypeTableColumns = (): Array> => [ @@ -48,11 +51,11 @@ export const getAlertsTypeTableColumns = (): Array - {ALERT_TYPE_LABEL[type as AlertType]} - + ); diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx index 9142ac1b2a226..3d7729a5e9ac7 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx @@ -7,7 +7,11 @@ import React from 'react'; import { EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { HostDetailsLink } from '../../../../common/components/links'; import type { HostRiskScoreColumns } from '.'; @@ -16,7 +20,6 @@ import { HostsTableType } from '../../store/model'; import type { RiskSeverity } from '../../../../../common/search_strategy'; import { RiskScoreFields } from '../../../../../common/search_strategy'; import { RiskScore } from '../../../components/risk_score/severity/common'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; export const getHostRiskScoreColumns = ({ dispatchSeverityUpdate, @@ -32,11 +35,11 @@ export const getHostRiskScoreColumns = ({ render: (hostName) => { if (hostName != null && hostName.length > 0) { return ( - - + ); } return getEmptyTagValue(); diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx index 188d5eb8b3809..1dcff6fbb0afd 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx @@ -7,7 +7,11 @@ import { EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui'; import React from 'react'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { HostDetailsLink } from '../../../../common/components/links'; import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; @@ -16,7 +20,6 @@ import * as i18n from './translations'; import type { Maybe, RiskSeverity } from '../../../../../common/search_strategy'; import { VIEW_HOSTS_BY_SEVERITY } from '../host_risk_score_table/translations'; import { RiskScore } from '../../../components/risk_score/severity/common'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; export const getHostsColumns = ( showRiskColumn: boolean, @@ -32,11 +35,11 @@ export const getHostsColumns = ( render: (hostName) => { if (hostName != null && hostName.length > 0) { return ( - - + ); } return getEmptyTagValue(); @@ -89,11 +92,11 @@ export const getHostsColumns = ( render: (hostOsName) => { if (hostOsName != null) { return ( - {hostOsName} - + ); } return getEmptyTagValue(); @@ -116,11 +119,11 @@ export const getHostsColumns = ( render: (hostOsVersion) => { if (hostOsVersion != null) { return ( - {hostOsVersion} - + ); } return getEmptyTagValue(); diff --git a/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx index 72e4268eb399e..c343742143ac2 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx @@ -8,7 +8,11 @@ import numeral from '@elastic/numeral'; import React from 'react'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; import type { NetworkDnsItem } from '../../../../../common/search_strategy'; import { NetworkDnsFields } from '../../../../../common/search_strategy'; import { escapeDataProviderId } from '../../../../common/components/drag_and_drop/helpers'; @@ -17,7 +21,7 @@ import type { Columns } from '../../../components/paginated_table'; import { PreferenceFormattedBytes } from '../../../../common/components/formatted_bytes'; import * as i18n from './translations'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; + export type NetworkDnsColumns = [ Columns, Columns, @@ -36,12 +40,12 @@ export const getNetworkDnsColumns = (): NetworkDnsColumns => [ render: (dnsName) => { if (dnsName != null) { return ( - [ }} > {defaultToEmptyTag(dnsName)} - + ); } else { return getEmptyTagValue(); diff --git a/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx index 2505337e47c3e..d7b443e1ab326 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx @@ -9,7 +9,11 @@ import { get } from 'lodash/fp'; import numeral from '@elastic/numeral'; import React from 'react'; import type { DataViewBase } from '@kbn/es-query'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; import { CountryFlagAndName } from '../source_destination/country_flag'; import type { NetworkTopCountriesEdges, @@ -22,7 +26,6 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value'; import type { Columns } from '../../../components/paginated_table'; import * as i18n from './translations'; import { PreferenceFormattedBytes } from '../../../../common/components/formatted_bytes'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; export type NetworkTopCountriesColumns = [ Columns, @@ -55,12 +58,12 @@ export const getNetworkTopCountriesColumns = ( const id = escapeDataProviderId(`${tableId}-table-${flowTarget}-country-${geo}`); if (geo != null) { return ( - - + ); } else { return getEmptyTagValue(); diff --git a/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx index cdb323640972d..6d11df641286b 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx @@ -9,7 +9,11 @@ import { get } from 'lodash/fp'; import numeral from '@elastic/numeral'; import React from 'react'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; import { CountryFlag } from '../source_destination/country_flag'; import type { AutonomousSystemItem, @@ -25,7 +29,6 @@ import type { Columns } from '../../../components/paginated_table'; import * as i18n from './translations'; import { getRowItemsWithActions } from '../../../../common/components/tables/helpers'; import { PreferenceFormattedBytes } from '../../../../common/components/formatted_bytes'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; export type NetworkTopNFlowColumns = [ Columns, @@ -63,12 +66,12 @@ export const getNetworkTopNFlowColumns = ( if (ip != null) { return ( <> - - + {geo && ( - {' '} {geo} - + )} ); diff --git a/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx index f7d605789b082..2e9771da07de6 100644 --- a/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx @@ -13,7 +13,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer } from '@elasti import { getEsQueryConfig } from '@kbn/data-plugin/common'; import { buildEsQuery } from '@kbn/es-query'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; import { AlertsByStatus } from '../../../../overview/components/detection_response/alerts_by_status'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { InputsModelId } from '../../../../common/store/inputs/constants'; @@ -53,7 +52,11 @@ import { useAlertsPrivileges } from '../../../../detections/containers/detection import { navTabsNetworkDetails } from './nav_tabs'; import { NetworkDetailsTabs } from './details_tabs'; import { useInstalledSecurityJobNameById } from '../../../../common/components/ml/hooks/use_installed_security_jobs'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../../common/components/cell_actions'; export { getTrailingBreadcrumbs } from './utils'; @@ -178,14 +181,14 @@ const NetworkDetailsComponent: React.FC = () => { /> } title={ - {ip} - + } > diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx index b2734cdf0513b..3fd6d86cc0b92 100644 --- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx @@ -7,7 +7,11 @@ import React from 'react'; import { EuiIcon, EuiLink, EuiText, EuiToolTip } from '@elastic/eui'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; +import { + SecurityCellActions, + SecurityCellActionsTrigger, + CellActionsMode, +} from '../../../../common/components/cell_actions'; import { escapeDataProviderId } from '../../../../common/components/drag_and_drop/helpers'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; import type { UserRiskScoreColumns } from '.'; @@ -17,7 +21,6 @@ import type { RiskSeverity } from '../../../../../common/search_strategy'; import { RiskScoreFields } from '../../../../../common/search_strategy'; import { UserDetailsLink } from '../../../../common/components/links'; import { UsersTableType } from '../../store/model'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; export const getUserRiskScoreColumns = ({ dispatchSeverityUpdate, @@ -34,12 +37,12 @@ export const getUserRiskScoreColumns = ({ if (userName != null && userName.length > 0) { const id = escapeDataProviderId(`user-risk-score-table-userName-${userName}`); return ( - - + ); } return getEmptyTagValue(); diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx index 8cf150387c1e9..141ce39008928 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx @@ -9,7 +9,6 @@ import React from 'react'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiLink, EuiIcon, EuiToolTip } from '@elastic/eui'; import { get } from 'lodash/fp'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; import styled from 'styled-components'; import { UsersTableType } from '../../../../explore/users/store/model'; import { getEmptyTagValue } from '../../../../common/components/empty_value'; @@ -24,14 +23,16 @@ import type { import { RiskScoreEntity, RiskScoreFields } from '../../../../../common/search_strategy'; import * as i18n from './translations'; import { FormattedCount } from '../../../../common/components/formatted_number'; -import { CELL_ACTIONS_DEFAULT_TRIGGER } from '../../../../../common/constants'; -import { ACTION_ID as FILTER_IN_ACTION_ID } from '../../../../actions/filter/default/filter_in'; -import { ACTION_ID as FILTER_OUT_ACTION_ID } from '../../../../actions/filter/default/filter_out'; -import { ACTION_ID as SHOW_TOP_N_ACTION_ID } from '../../../../actions/show_top_n/default/show_top_n'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, + SecurityCellActionType, +} from '../../../../common/components/cell_actions'; type HostRiskScoreColumns = Array>; -const StyledCellActions = styled(CellActions)` +const StyledCellActions = styled(SecurityCellActions)` padding-left: ${({ theme }) => theme.eui.euiSizeS}; `; @@ -55,10 +56,13 @@ export const getRiskScoreColumns = ( value: entityName, type: 'keyword', }} - triggerId={CELL_ACTIONS_DEFAULT_TRIGGER} + triggerId={SecurityCellActionsTrigger.DEFAULT} mode={CellActionsMode.INLINE} visibleCellActions={2} - disabledActions={[SHOW_TOP_N_ACTION_ID, FILTER_IN_ACTION_ID, FILTER_OUT_ACTION_ID]} + disabledActionTypes={[ + SecurityCellActionType.FILTER, + SecurityCellActionType.SHOW_TOP_N, + ]} /> ) : ( @@ -70,9 +74,12 @@ export const getRiskScoreColumns = ( value: entityName, type: 'keyword', }} - triggerId={CELL_ACTIONS_DEFAULT_TRIGGER} + triggerId={SecurityCellActionsTrigger.DEFAULT} mode={CellActionsMode.INLINE} - disabledActions={[SHOW_TOP_N_ACTION_ID, FILTER_IN_ACTION_ID, FILTER_OUT_ACTION_ID]} + disabledActionTypes={[ + SecurityCellActionType.FILTER, + SecurityCellActionType.SHOW_TOP_N, + ]} /> ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx index ed02581695834..6238392e888be 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx @@ -11,8 +11,12 @@ import { getOr } from 'lodash/fp'; import React, { useCallback, Fragment, useMemo, useState, useContext } from 'react'; import styled from 'styled-components'; -import { CellActions, CellActionsMode } from '@kbn/cell-actions'; import type { HostEcs } from '@kbn/securitysolution-ecs'; +import { + SecurityCellActions, + CellActionsMode, + SecurityCellActionsTrigger, +} from '../../../common/components/cell_actions'; import type { AutonomousSystem, FlowTarget, @@ -26,10 +30,6 @@ import { FormattedRelativePreferenceDate } from '../../../common/components/form import { HostDetailsLink, ReputationLink, WhoIsLink } from '../../../common/components/links'; import { Spacer } from '../../../common/components/page'; import * as i18n from '../../../explore/network/components/details/translations'; -import { - CELL_ACTIONS_DEFAULT_TRIGGER, - CELL_ACTIONS_TIMELINE_TRIGGER, -} from '../../../../common/constants'; import { TimelineContext } from '../timeline'; const DraggableContainerFlexGroup = styled(EuiFlexGroup)` @@ -315,23 +315,24 @@ export const MoreContainer = React.memo( if (typeof value === 'string' && fieldName != null) { acc.push( - <>{render ? render(value) : defaultToEmptyTag(value)} - + ); } From 3d60b94351d04a03f9cfa14fcc2b9620f3f292ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Wed, 15 Feb 2023 12:45:12 +0100 Subject: [PATCH 08/68] [Synthetics UI] Remove dead link to errors (#150838) Closes #150495 ## Summary Remove a dead link to a non existing errors page. Screen Shot 2023-02-07 at 4 14 44 PM --- .../overview/overview/overview_errors/overview_errors.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx index 636da377442fa..bfc57a2b55778 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx @@ -20,7 +20,6 @@ import { selectOverviewStatus } from '../../../../../state/overview_status'; import { OverviewErrorsSparklines } from './overview_errors_sparklines'; import { useAbsoluteDate } from '../../../../../hooks'; import { OverviewErrorsCount } from './overview_errors_count'; -import { ErrorsLink } from '../../../../common/links/view_errors'; export function OverviewErrors() { const { status } = useSelector(selectOverviewStatus); @@ -53,9 +52,6 @@ export function OverviewErrors() { monitorIds={status?.enabledMonitorQueryIds ?? []} /> - - - )} From 4076ecef2d4a6f88ae49731f00d8fcbf0adbcc11 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 15 Feb 2023 13:32:46 +0100 Subject: [PATCH 09/68] [CM] Client-side content client: setup `update` `delete` `search` methods (#151041) Nothing to write home about - just a bit more boilerplate setup code. Method signatures and RPC related interfaces are expected to be changed later with an initial RPC layer implementation. - Follow-up to https://github.com/elastic/kibana/pull/150171, adding a dummy setup for `update` `delete` and `search` methods. **The `IN` and `OUT` args will be changes when we glue these to the rpc service and its types** - Also clean up unit tests a bit to have setup logic contained in a helper instead of scattered in global scope --- .../content_management/common/index.ts | 11 ++- src/plugins/content_management/common/rpc.ts | 78 ++++++++++++++++++- .../content_client/content_client.test.ts | 73 +++++++++++++++-- .../public/content_client/content_client.tsx | 27 ++++++- .../content_client_mutation_hooks.test.tsx | 64 +++++++++++---- .../content_client_mutation_hooks.tsx | 20 ++++- .../content_client_query_hooks.test.tsx | 42 +++++++--- .../content_client_query_hooks.tsx | 23 +++++- .../public/content_client/index.ts | 12 ++- .../public/crud_client/crud_client.mock.ts | 3 + .../public/crud_client/crud_client.ts | 5 +- .../public/rpc_client/rpc_client.ts | 24 +++++- 12 files changed, 339 insertions(+), 43 deletions(-) diff --git a/src/plugins/content_management/common/index.ts b/src/plugins/content_management/common/index.ts index 3746f26a2cf3d..1670078ae8d79 100644 --- a/src/plugins/content_management/common/index.ts +++ b/src/plugins/content_management/common/index.ts @@ -7,5 +7,14 @@ */ export { PLUGIN_ID, API_ENDPOINT } from './constants'; -export type { ProcedureSchemas, ProcedureName, GetIn, CreateIn } from './rpc'; +export type { + ProcedureSchemas, + ProcedureName, + GetIn, + CreateIn, + SearchIn, + SearchOut, + DeleteIn, + UpdateIn, +} from './rpc'; export { procedureNames, schemas as rpcSchemas } from './rpc'; diff --git a/src/plugins/content_management/common/rpc.ts b/src/plugins/content_management/common/rpc.ts index 8e03dc886a3f0..aa0d6a3b2e2c8 100644 --- a/src/plugins/content_management/common/rpc.ts +++ b/src/plugins/content_management/common/rpc.ts @@ -12,7 +12,7 @@ export interface ProcedureSchemas { out?: Type | false; } -export const procedureNames = ['get', 'create'] as const; +export const procedureNames = ['get', 'create', 'update', 'delete', 'search'] as const; export type ProcedureName = typeof procedureNames[number]; @@ -64,9 +64,85 @@ export interface CreateIn< options?: Options; } +// -- Update content +const updateSchemas: ProcedureSchemas = { + in: schema.object( + { + contentType: schema.string(), + data: schema.object({}, { unknowns: 'allow' }), + options: schema.maybe(schema.object({}, { unknowns: 'allow' })), + }, + { unknowns: 'forbid' } + ), + out: schema.maybe(schema.object({}, { unknowns: 'allow' })), +}; + +export interface UpdateIn< + T extends string = string, + Data extends object = Record, + Options extends object = any +> { + contentType: T; + data: Data; + options?: Options; +} + +// -- Delete content +const deleteSchemas: ProcedureSchemas = { + in: schema.object( + { + contentType: schema.string(), + data: schema.object({}, { unknowns: 'allow' }), + options: schema.maybe(schema.object({}, { unknowns: 'allow' })), + }, + { unknowns: 'forbid' } + ), + out: schema.maybe(schema.object({}, { unknowns: 'allow' })), +}; + +export interface DeleteIn< + T extends string = string, + Data extends object = Record, + Options extends object = any +> { + contentType: T; + data: Data; + options?: Options; +} + +// -- Search content +const searchSchemas: ProcedureSchemas = { + in: schema.object( + { + contentType: schema.string(), + data: schema.object({}, { unknowns: 'allow' }), + options: schema.maybe(schema.object({}, { unknowns: 'allow' })), + }, + { unknowns: 'forbid' } + ), + out: schema.object({ hits: schema.arrayOf(schema.object({}, { unknowns: 'allow' })) }), +}; + +export interface SearchIn< + T extends string = string, + Params extends object = Record, + Options extends object = any +> { + contentType: T; + params: Params; + options?: Options; +} + +export interface SearchOut> { + hits: Data[]; +} + export const schemas: { [key in ProcedureName]: ProcedureSchemas; } = { get: getSchemas, create: createSchemas, + update: updateSchemas, + delete: deleteSchemas, + search: searchSchemas, }; diff --git a/src/plugins/content_management/public/content_client/content_client.test.ts b/src/plugins/content_management/public/content_client/content_client.test.ts index e8465206b5e0b..02bd67a1da8da 100644 --- a/src/plugins/content_management/public/content_client/content_client.test.ts +++ b/src/plugins/content_management/public/content_client/content_client.test.ts @@ -8,20 +8,19 @@ import { lastValueFrom } from 'rxjs'; import { takeWhile, toArray } from 'rxjs/operators'; -import type { CrudClient } from '../crud_client'; import { createCrudClientMock } from '../crud_client/crud_client.mock'; import { ContentClient } from './content_client'; -import type { GetIn, CreateIn } from '../../common'; +import type { GetIn, CreateIn, UpdateIn, DeleteIn, SearchIn, SearchOut } from '../../common'; -let contentClient: ContentClient; -let crudClient: jest.Mocked; -beforeEach(() => { - crudClient = createCrudClientMock(); - contentClient = new ContentClient(() => crudClient); -}); +const setup = () => { + const crudClient = createCrudClientMock(); + const contentClient = new ContentClient(() => crudClient); + return { crudClient, contentClient }; +}; describe('#get', () => { it('calls rpcClient.get with input and returns output', async () => { + const { crudClient, contentClient } = setup(); const input: GetIn = { id: 'test', contentType: 'testType' }; const output = { test: 'test' }; crudClient.get.mockResolvedValueOnce(output); @@ -30,6 +29,7 @@ describe('#get', () => { }); it('calls rpcClient.get$ with input and returns output', async () => { + const { crudClient, contentClient } = setup(); const input: GetIn = { id: 'test', contentType: 'testType' }; const output = { test: 'test' }; crudClient.get.mockResolvedValueOnce(output); @@ -52,6 +52,7 @@ describe('#get', () => { describe('#create', () => { it('calls rpcClient.create with input and returns output', async () => { + const { crudClient, contentClient } = setup(); const input: CreateIn = { contentType: 'testType', data: { foo: 'bar' } }; const output = { test: 'test' }; crudClient.create.mockResolvedValueOnce(output); @@ -60,3 +61,59 @@ describe('#create', () => { expect(crudClient.create).toBeCalledWith(input); }); }); + +describe('#update', () => { + it('calls rpcClient.update with input and returns output', async () => { + const { crudClient, contentClient } = setup(); + const input: UpdateIn = { contentType: 'testType', data: { id: 'test', foo: 'bar' } }; + const output = { test: 'test' }; + crudClient.update.mockResolvedValueOnce(output); + + expect(await contentClient.update(input)).toEqual(output); + expect(crudClient.update).toBeCalledWith(input); + }); +}); + +describe('#delete', () => { + it('calls rpcClient.delete with input and returns output', async () => { + const { crudClient, contentClient } = setup(); + const input: DeleteIn = { contentType: 'testType', data: { id: 'test' } }; + const output = { test: 'test' }; + crudClient.delete.mockResolvedValueOnce(output); + + expect(await contentClient.delete(input)).toEqual(output); + expect(crudClient.delete).toBeCalledWith(input); + }); +}); + +describe('#search', () => { + it('calls rpcClient.search with input and returns output', async () => { + const { crudClient, contentClient } = setup(); + const input: SearchIn = { contentType: 'testType', params: {} }; + const output: SearchOut = { hits: [{ test: 'test' }] }; + crudClient.search.mockResolvedValueOnce(output); + expect(await contentClient.search(input)).toEqual(output); + expect(crudClient.search).toBeCalledWith(input); + }); + + it('calls rpcClient.search$ with input and returns output', async () => { + const { crudClient, contentClient } = setup(); + const input: SearchIn = { contentType: 'testType', params: {} }; + const output: SearchOut = { hits: [{ test: 'test' }] }; + crudClient.search.mockResolvedValueOnce(output); + const search$ = contentClient.search$(input).pipe( + takeWhile((result) => { + return result.data == null; + }, true), + toArray() + ); + + const [loadingState, loadedState] = await lastValueFrom(search$); + + expect(loadingState.isLoading).toBe(true); + expect(loadingState.data).toBeUndefined(); + + expect(loadedState.isLoading).toBe(false); + expect(loadedState.data).toEqual(output); + }); +}); diff --git a/src/plugins/content_management/public/content_client/content_client.tsx b/src/plugins/content_management/public/content_client/content_client.tsx index e10af79ffefda..4a4952a082060 100644 --- a/src/plugins/content_management/public/content_client/content_client.tsx +++ b/src/plugins/content_management/public/content_client/content_client.tsx @@ -9,13 +9,16 @@ import { QueryClient } from '@tanstack/react-query'; import { createQueryObservable } from './query_observable'; import type { CrudClient } from '../crud_client'; -import type { CreateIn, GetIn } from '../../common'; +import type { CreateIn, GetIn, UpdateIn, DeleteIn, SearchIn, SearchOut } from '../../common'; const queryKeyBuilder = { all: (type: string) => [type] as const, item: (type: string, id: string) => { return [...queryKeyBuilder.all(type), id] as const; }, + search: (type: string, params: unknown) => { + return [...queryKeyBuilder.all(type), 'search', params] as const; + }, }; const createQueryOptionBuilder = ({ @@ -30,6 +33,12 @@ const createQueryOptionBuilder = ({ queryFn: () => crudClientProvider(input.contentType).get(input), }; }, + search: (input: I) => { + return { + queryKey: queryKeyBuilder.search(input.contentType, input.params), + queryFn: () => crudClientProvider(input.contentType).search(input), + }; + }, }; }; @@ -55,4 +64,20 @@ export class ContentClient { create(input: I): Promise { return this.crudClientProvider(input.contentType).create(input); } + + update(input: I): Promise { + return this.crudClientProvider(input.contentType).update(input); + } + + delete(input: I): Promise { + return this.crudClientProvider(input.contentType).delete(input); + } + + search(input: I): Promise { + return this.crudClientProvider(input.contentType).search(input); + } + + search$(input: I) { + return createQueryObservable(this.queryClient, this.queryOptionBuilder.search(input)); + } } diff --git a/src/plugins/content_management/public/content_client/content_client_mutation_hooks.test.tsx b/src/plugins/content_management/public/content_client/content_client_mutation_hooks.test.tsx index dfbf960e605d0..ca1d8fe8f8e29 100644 --- a/src/plugins/content_management/public/content_client/content_client_mutation_hooks.test.tsx +++ b/src/plugins/content_management/public/content_client/content_client_mutation_hooks.test.tsx @@ -10,24 +10,32 @@ import React from 'react'; import { renderHook } from '@testing-library/react-hooks'; import { ContentClientProvider } from './content_client_context'; import { ContentClient } from './content_client'; -import { CrudClient } from '../crud_client'; import { createCrudClientMock } from '../crud_client/crud_client.mock'; -import { useCreateContentMutation } from './content_client_mutation_hooks'; -import type { CreateIn } from '../../common'; - -let contentClient: ContentClient; -let crudClient: jest.Mocked; -beforeEach(() => { - crudClient = createCrudClientMock(); - contentClient = new ContentClient(() => crudClient); -}); +import { + useCreateContentMutation, + useUpdateContentMutation, + useDeleteContentMutation, +} from './content_client_mutation_hooks'; +import type { CreateIn, UpdateIn, DeleteIn } from '../../common'; + +const setup = () => { + const crudClient = createCrudClientMock(); + const contentClient = new ContentClient(() => crudClient); + + const Wrapper: React.FC = ({ children }) => ( + {children} + ); -const Wrapper: React.FC = ({ children }) => ( - {children} -); + return { + Wrapper, + contentClient, + crudClient, + }; +}; describe('useCreateContentMutation', () => { test('should call rpcClient.create with input and resolve with output', async () => { + const { Wrapper, crudClient } = setup(); const input: CreateIn = { contentType: 'testType', data: { foo: 'bar' } }; const output = { test: 'test' }; crudClient.create.mockResolvedValueOnce(output); @@ -39,3 +47,33 @@ describe('useCreateContentMutation', () => { expect(result.current.data).toEqual(output); }); }); + +describe('useUpdateContentMutation', () => { + test('should call rpcClient.update with input and resolve with output', async () => { + const { Wrapper, crudClient } = setup(); + const input: UpdateIn = { contentType: 'testType', data: { foo: 'bar' } }; + const output = { test: 'test' }; + crudClient.update.mockResolvedValueOnce(output); + const { result, waitFor } = renderHook(() => useUpdateContentMutation(), { wrapper: Wrapper }); + result.current.mutate(input); + + await waitFor(() => result.current.isSuccess); + + expect(result.current.data).toEqual(output); + }); +}); + +describe('useDeleteContentMutation', () => { + test('should call rpcClient.delete with input and resolve with output', async () => { + const { Wrapper, crudClient } = setup(); + const input: DeleteIn = { contentType: 'testType', data: { foo: 'bar' } }; + const output = { test: 'test' }; + crudClient.delete.mockResolvedValueOnce(output); + const { result, waitFor } = renderHook(() => useDeleteContentMutation(), { wrapper: Wrapper }); + result.current.mutate(input); + + await waitFor(() => result.current.isSuccess); + + expect(result.current.data).toEqual(output); + }); +}); diff --git a/src/plugins/content_management/public/content_client/content_client_mutation_hooks.tsx b/src/plugins/content_management/public/content_client/content_client_mutation_hooks.tsx index 372b05b2ed093..dcde9ae1c8fe4 100644 --- a/src/plugins/content_management/public/content_client/content_client_mutation_hooks.tsx +++ b/src/plugins/content_management/public/content_client/content_client_mutation_hooks.tsx @@ -8,7 +8,7 @@ import { useMutation } from '@tanstack/react-query'; import { useContentClient } from './content_client_context'; -import type { CreateIn } from '../../common'; +import type { CreateIn, UpdateIn, DeleteIn } from '../../common'; export const useCreateContentMutation = () => { const contentClient = useContentClient(); @@ -18,3 +18,21 @@ export const useCreateContentMutation = () => { + const contentClient = useContentClient(); + return useMutation({ + mutationFn: (input: I) => { + return contentClient.update(input); + }, + }); +}; + +export const useDeleteContentMutation = () => { + const contentClient = useContentClient(); + return useMutation({ + mutationFn: (input: I) => { + return contentClient.delete(input); + }, + }); +}; diff --git a/src/plugins/content_management/public/content_client/content_client_query_hooks.test.tsx b/src/plugins/content_management/public/content_client/content_client_query_hooks.test.tsx index 4b88b32b5e5c8..7ed1ff8412a45 100644 --- a/src/plugins/content_management/public/content_client/content_client_query_hooks.test.tsx +++ b/src/plugins/content_management/public/content_client/content_client_query_hooks.test.tsx @@ -10,24 +10,28 @@ import React from 'react'; import { renderHook } from '@testing-library/react-hooks'; import { ContentClientProvider } from './content_client_context'; import { ContentClient } from './content_client'; -import { CrudClient } from '../crud_client'; import { createCrudClientMock } from '../crud_client/crud_client.mock'; -import { useGetContentQuery } from './content_client_query_hooks'; -import type { GetIn } from '../../common'; +import { useGetContentQuery, useSearchContentQuery } from './content_client_query_hooks'; +import type { GetIn, SearchIn, SearchOut } from '../../common'; -let contentClient: ContentClient; -let crudClient: jest.Mocked; -beforeEach(() => { - crudClient = createCrudClientMock(); - contentClient = new ContentClient(() => crudClient); -}); +const setup = () => { + const crudClient = createCrudClientMock(); + const contentClient = new ContentClient(() => crudClient); + + const Wrapper: React.FC = ({ children }) => ( + {children} + ); -const Wrapper: React.FC = ({ children }) => ( - {children} -); + return { + Wrapper, + contentClient, + crudClient, + }; +}; describe('useGetContentQuery', () => { test('should call rpcClient.get with input and resolve with output', async () => { + const { crudClient, Wrapper } = setup(); const input: GetIn = { id: 'test', contentType: 'testType' }; const output = { test: 'test' }; crudClient.get.mockResolvedValueOnce(output); @@ -36,3 +40,17 @@ describe('useGetContentQuery', () => { expect(result.current.data).toEqual(output); }); }); + +describe('useSearchContentQuery', () => { + test('should call rpcClient.search with input and resolve with output', async () => { + const { crudClient, Wrapper } = setup(); + const input: SearchIn = { contentType: 'testType', params: {} }; + const output: SearchOut = { hits: [{ test: 'test' }] }; + crudClient.search.mockResolvedValueOnce(output); + const { result, waitFor } = renderHook(() => useSearchContentQuery(input), { + wrapper: Wrapper, + }); + await waitFor(() => result.current.isSuccess); + expect(result.current.data).toEqual(output); + }); +}); diff --git a/src/plugins/content_management/public/content_client/content_client_query_hooks.tsx b/src/plugins/content_management/public/content_client/content_client_query_hooks.tsx index 09ee31bdd14f3..8231d7bfc9390 100644 --- a/src/plugins/content_management/public/content_client/content_client_query_hooks.tsx +++ b/src/plugins/content_management/public/content_client/content_client_query_hooks.tsx @@ -8,7 +8,7 @@ import { useQuery, QueryObserverOptions } from '@tanstack/react-query'; import { useContentClient } from './content_client_context'; -import type { GetIn } from '../../common'; +import type { GetIn, SearchIn, SearchOut } from '../../common'; /** * Exposed `useQuery` options @@ -18,7 +18,7 @@ export type QueryOptions = Pick; /** * * @param input - get content identifier like "id" and "contentType" - * @param queryOptions - + * @param queryOptions - query options */ export const useGetContentQuery = ( input: I, @@ -30,3 +30,22 @@ export const useGetContentQuery = ( ...queryOptions, }); }; + +/** + * + * @param input - get content identifier like "id" and "contentType" + * @param queryOptions - query options + */ +export const useSearchContentQuery = < + I extends SearchIn = SearchIn, + O extends SearchOut = SearchOut +>( + input: I, + queryOptions?: QueryOptions +) => { + const contentClient = useContentClient(); + return useQuery({ + ...contentClient.queryOptionBuilder.search(input), + ...queryOptions, + }); +}; diff --git a/src/plugins/content_management/public/content_client/index.ts b/src/plugins/content_management/public/content_client/index.ts index 329df9c596452..2904b19668dfd 100644 --- a/src/plugins/content_management/public/content_client/index.ts +++ b/src/plugins/content_management/public/content_client/index.ts @@ -8,5 +8,13 @@ export { ContentClient } from './content_client'; export { ContentClientProvider, useContentClient } from './content_client_context'; -export { useGetContentQuery } from './content_client_query_hooks'; -export { useCreateContentMutation } from './content_client_mutation_hooks'; +export { + useGetContentQuery, + useSearchContentQuery, + type QueryOptions, +} from './content_client_query_hooks'; +export { + useCreateContentMutation, + useUpdateContentMutation, + useDeleteContentMutation, +} from './content_client_mutation_hooks'; diff --git a/src/plugins/content_management/public/crud_client/crud_client.mock.ts b/src/plugins/content_management/public/crud_client/crud_client.mock.ts index abcb551b33cb9..2b2bead4ea462 100644 --- a/src/plugins/content_management/public/crud_client/crud_client.mock.ts +++ b/src/plugins/content_management/public/crud_client/crud_client.mock.ts @@ -12,6 +12,9 @@ export const createCrudClientMock = (): jest.Mocked => { const mock: jest.Mocked = { get: jest.fn((input) => Promise.resolve({} as any)), create: jest.fn((input) => Promise.resolve({} as any)), + update: jest.fn((input) => Promise.resolve({} as any)), + delete: jest.fn((input) => Promise.resolve({} as any)), + search: jest.fn((input) => Promise.resolve({ hits: [] } as any)), }; return mock; }; diff --git a/src/plugins/content_management/public/crud_client/crud_client.ts b/src/plugins/content_management/public/crud_client/crud_client.ts index f233eb3f62e37..e2c5fd8fb86a5 100644 --- a/src/plugins/content_management/public/crud_client/crud_client.ts +++ b/src/plugins/content_management/public/crud_client/crud_client.ts @@ -6,9 +6,12 @@ * Side Public License, v 1. */ -import type { GetIn, CreateIn } from '../../common'; +import type { GetIn, CreateIn, UpdateIn, DeleteIn, SearchIn, SearchOut } from '../../common'; export interface CrudClient { get(input: I): Promise; create(input: I): Promise; + update(input: I): Promise; + delete(input: I): Promise; + search(input: I): Promise; } diff --git a/src/plugins/content_management/public/rpc_client/rpc_client.ts b/src/plugins/content_management/public/rpc_client/rpc_client.ts index b5b7b7804d9d1..5ce2e781dcbb1 100644 --- a/src/plugins/content_management/public/rpc_client/rpc_client.ts +++ b/src/plugins/content_management/public/rpc_client/rpc_client.ts @@ -8,7 +8,15 @@ import { HttpSetup } from '@kbn/core/public'; import { API_ENDPOINT } from '../../common'; -import type { GetIn, CreateIn, ProcedureName } from '../../common'; +import type { + GetIn, + CreateIn, + UpdateIn, + DeleteIn, + SearchIn, + SearchOut, + ProcedureName, +} from '../../common'; import type { CrudClient } from '../crud_client/crud_client'; export class RpcClient implements CrudClient { @@ -22,6 +30,20 @@ export class RpcClient implements CrudClient { return this.sendMessage('create', input); } + public update(input: I): Promise { + return this.sendMessage('update', input); + } + + public delete(input: I): Promise { + return this.sendMessage('delete', input); + } + + public search( + input: I + ): Promise { + return this.sendMessage('search', input); + } + private sendMessage = async (name: ProcedureName, input: any): Promise => { const { result } = await this.http.post<{ result: any }>(`${API_ENDPOINT}/${name}`, { body: JSON.stringify(input), From fab938e5ec1bb448b80dddc2786306686898f98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Wed, 15 Feb 2023 14:09:05 +0100 Subject: [PATCH 10/68] [APM] Make `service.name` required in `AnnotationsContextProvider` (#151281) `service.name` was not being passed to `AnnotationsContextProvider` in the `TransactionCharts`. The result of this change is that annotations will show up on transaction charts (they didn't show up before) --- .../mobile/transaction_overview/transaction_charts.tsx | 1 + .../public/components/app/transaction_details/index.tsx | 9 +++++++-- .../public/components/app/transaction_overview/index.tsx | 9 +++++++-- .../shared/charts/transaction_charts/index.tsx | 3 +++ .../public/context/annotations/annotations_context.tsx | 4 ++-- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_charts.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_charts.tsx index a80f0ebfbced3..86d693aaf5955 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_charts.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/transaction_charts.tsx @@ -36,6 +36,7 @@ export function MobileTransactionCharts({ }) { return ( )} { From 2013e0d8e5f448f761202fc6504554aa531f7ad4 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Wed, 15 Feb 2023 08:24:30 -0500 Subject: [PATCH 11/68] fix(ftr): o11y functional tests flakiness (#151210) --- .../apps/observability/pages/alerts/add_to_case.ts | 7 +++---- .../apps/observability/pages/alerts/index.ts | 4 ++-- .../apps/observability/pages/alerts/table_storage.ts | 8 ++++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/add_to_case.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/add_to_case.ts index 3c2b6b4403770..a575700e2cc9a 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/alerts/add_to_case.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/add_to_case.ts @@ -17,16 +17,15 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); - await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await esArchiver.load('x-pack/test/functional/es_archives/infra/simple_logs'); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await esArchiver.unload('x-pack/test/functional/es_archives/infra/simple_logs'); await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); }); - // FLAKY: https://github.com/elastic/kibana/issues/133799 - describe.skip('When user has all priviledges for cases', () => { + describe('When user has all priviledges for cases', () => { before(async () => { await observability.users.setTestUserRole( observability.users.defineBasicObservabilityRole({ diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts index 99c41f250ab07..d2b129b4baaa3 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts @@ -49,12 +49,12 @@ export default ({ getService }: FtrProviderContext) => { describe('Alerts table', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await esArchiver.load('x-pack/test/functional/es_archives/infra/simple_logs'); await observability.alerts.common.navigateToTimeWithData(); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await esArchiver.unload('x-pack/test/functional/es_archives/infra/simple_logs'); }); it('Renders the table', async () => { diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/table_storage.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/table_storage.ts index e091f8ddeb7bf..5f96f9d707c7b 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/alerts/table_storage.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/table_storage.ts @@ -19,17 +19,16 @@ export default ({ getService, getPageObject }: FtrProviderContext) => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); - await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); - - await observability.alerts.common.navigateToTimeWithData(); + await esArchiver.load('x-pack/test/functional/es_archives/infra/simple_logs'); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await esArchiver.unload('x-pack/test/functional/es_archives/infra/simple_logs'); await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); }); it('remembers column changes', async () => { + await observability.alerts.common.navigateToTimeWithData(); const durationColumnButton = await testSubjects.find( 'dataGridHeaderCellActionButton-kibana.alert.duration.us' ); @@ -50,6 +49,7 @@ export default ({ getService, getPageObject }: FtrProviderContext) => { }); it('remembers sorting changes', async () => { + await observability.alerts.common.navigateToTimeWithData(); const timestampColumnButton = await testSubjects.find( 'dataGridHeaderCellActionButton-@timestamp' ); From 9f820b8b42e95bd191e5d0ce703efd8e23dfe635 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 15 Feb 2023 14:25:45 +0100 Subject: [PATCH 12/68] [Fleet] Use new dataView API for agent logs filters (#151030) Closes https://github.com/elastic/kibana/issues/135970 ## Summary Use `dataView` API in agent dataset and logs filters. These two components were composing their own dataView-like objects. Now they compose `DataViewSpecs` and instantiate them with `DataViews.create`. ## Testing steps Run an agent, navigate to agent logs and verify that the "dataset" and "log level" filter work as usual: Screenshot 2023-02-14 at 17 11 38 Screenshot 2023-02-14 at 17 11 30 ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../components/agent_logs/constants.tsx | 2 + .../agent_logs/filter_dataset.test.tsx | 62 +++++++++++++++++++ .../components/agent_logs/filter_dataset.tsx | 24 ++++--- .../agent_logs/filter_log_level.test.tsx | 55 ++++++++++++++++ .../agent_logs/filter_log_level.tsx | 24 ++++--- 5 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.test.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.test.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx index 18af09c48f220..0225cec66a3cd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx @@ -24,11 +24,13 @@ export const DATASET_FIELD = { name: 'data_stream.dataset', type: 'string', aggregatable: true, + searchable: true, }; export const LOG_LEVEL_FIELD = { name: 'log.level', type: 'string', aggregatable: true, + searchable: true, }; export const DEFAULT_DATE_RANGE = { start: 'now-1d', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.test.tsx new file mode 100644 index 0000000000000..2a942a2c0c0cd --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render, act, fireEvent } from '@testing-library/react'; + +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +import { DatasetFilter } from './filter_dataset'; + +const renderComponent = (props: React.ComponentProps) => { + return render( + + + + ); +}; + +jest.mock('../../../../../hooks', () => ({ + ...jest.requireActual('../../../../../hooks'), + useStartServices: jest.fn().mockReturnValue({ + data: { + dataViews: { + getFieldsForWildcard: jest.fn().mockResolvedValue([]), + create: jest.fn().mockResolvedValue([]), + }, + }, + unifiedSearch: { + autocomplete: { + getValueSuggestions: jest + .fn() + .mockResolvedValue([ + 'elastic_agent', + 'elastic_agent.filebeat', + 'elastic_agent.fleet_server', + 'elastic_agent.metricbeat', + ]), + }, + }, + }), +})); + +describe('DatasetFilter', () => { + const { getByRole, getByText } = renderComponent({ + selectedDatasets: [], + onToggleDataset: () => {}, + }); + + it('Renders all statuses', () => { + act(() => { + fireEvent.click(getByRole('button')); + }); + + expect(getByText('elastic_agent')).toBeInTheDocument(); + expect(getByText('elastic_agent.filebeat')).toBeInTheDocument(); + expect(getByText('elastic_agent.fleet_server')).toBeInTheDocument(); + expect(getByText('elastic_agent.metricbeat')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx index b4677761be1a2..9aafb11d999f1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx @@ -8,7 +8,7 @@ import React, { memo, useState, useEffect, useCallback } from 'react'; import { EuiPopover, EuiFilterButton, EuiFilterSelectItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import type { DataViewField, FieldSpec } from '@kbn/data-views-plugin/public'; import { useStartServices } from '../../../../../hooks'; @@ -18,7 +18,7 @@ export const DatasetFilter: React.FunctionComponent<{ selectedDatasets: string[]; onToggleDataset: (dataset: string) => void; }> = memo(({ selectedDatasets, onToggleDataset }) => { - const { unifiedSearch } = useStartServices(); + const { unifiedSearch, data } = useStartServices(); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const [datasetValues, setDatasetValues] = useState([AGENT_DATASET]); @@ -30,11 +30,20 @@ export const DatasetFilter: React.FunctionComponent<{ const fetchValues = async () => { setIsLoading(true); try { + const fields: FieldSpec[] = await data.dataViews.getFieldsForWildcard({ + pattern: AGENT_LOG_INDEX_PATTERN, + }); + const fieldsMap = fields.reduce((acc: Record, curr: FieldSpec) => { + acc[curr.name] = curr; + return acc; + }, {}); + const newDataView = await data.dataViews.create({ + title: AGENT_LOG_INDEX_PATTERN, + fields: fieldsMap, + }); + const values = await unifiedSearch.autocomplete.getValueSuggestions({ - indexPattern: { - title: AGENT_LOG_INDEX_PATTERN, - fields: [DATASET_FIELD], - } as DataView, + indexPattern: newDataView, field: DATASET_FIELD as DataViewField, query: '', }); @@ -45,12 +54,13 @@ export const DatasetFilter: React.FunctionComponent<{ setIsLoading(false); }; fetchValues(); - }, [unifiedSearch.autocomplete]); + }, [data.dataViews, unifiedSearch.autocomplete]); return ( ) => { + return render( + + + + ); +}; + +jest.mock('../../../../../hooks', () => ({ + ...jest.requireActual('../../../../../hooks'), + useStartServices: jest.fn().mockReturnValue({ + data: { + dataViews: { + getFieldsForWildcard: jest.fn().mockResolvedValue([]), + create: jest.fn().mockResolvedValue([]), + }, + }, + unifiedSearch: { + autocomplete: { + getValueSuggestions: jest.fn().mockResolvedValue(['error', 'warn', 'info', 'debug']), + }, + }, + }), +})); + +describe('LogLevelFilter', () => { + const { getByRole, getByText } = renderComponent({ + selectedLevels: [], + onToggleLevel: () => {}, + }); + + it('Renders all statuses', () => { + act(() => { + fireEvent.click(getByRole('button')); + }); + + expect(getByText('error')).toBeInTheDocument(); + expect(getByText('warn')).toBeInTheDocument(); + expect(getByText('info')).toBeInTheDocument(); + expect(getByText('debug')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx index b512b0a5643d6..1fca50180b682 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx @@ -8,7 +8,7 @@ import React, { memo, useState, useEffect, useCallback } from 'react'; import { EuiPopover, EuiFilterButton, EuiFilterSelectItem, EuiIcon, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import type { DataViewField, FieldSpec } from '@kbn/data-views-plugin/public'; import { useStartServices } from '../../../../../hooks'; @@ -29,7 +29,7 @@ export const LogLevelFilter: React.FunctionComponent<{ selectedLevels: string[]; onToggleLevel: (level: string) => void; }> = memo(({ selectedLevels, onToggleLevel }) => { - const { unifiedSearch } = useStartServices(); + const { unifiedSearch, data } = useStartServices(); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const [levelValues, setLevelValues] = useState([]); @@ -41,11 +41,20 @@ export const LogLevelFilter: React.FunctionComponent<{ const fetchValues = async () => { setIsLoading(true); try { + const fields: FieldSpec[] = await data.dataViews.getFieldsForWildcard({ + pattern: AGENT_LOG_INDEX_PATTERN, + }); + const fieldsMap = fields.reduce((acc: Record, curr: FieldSpec) => { + acc[curr.name] = curr; + return acc; + }, {}); + const newDataView = await data.dataViews.create({ + title: AGENT_LOG_INDEX_PATTERN, + fields: fieldsMap, + }); + const values: string[] = await unifiedSearch.autocomplete.getValueSuggestions({ - indexPattern: { - title: AGENT_LOG_INDEX_PATTERN, - fields: [LOG_LEVEL_FIELD], - } as DataView, + indexPattern: newDataView, field: LOG_LEVEL_FIELD as DataViewField, query: '', }); @@ -56,7 +65,7 @@ export const LogLevelFilter: React.FunctionComponent<{ setIsLoading(false); }; fetchValues(); - }, [unifiedSearch.autocomplete]); + }, [data.dataViews, unifiedSearch.autocomplete]); const noLogsFound = (
@@ -85,6 +94,7 @@ export const LogLevelFilter: React.FunctionComponent<{ Date: Wed, 15 Feb 2023 13:26:08 +0000 Subject: [PATCH 13/68] Change description for metadata field in ingest pipelines (#150935) ## Summary Improves the description of the `_meta` field in the Ingest pipeline form as per the decision made in https://github.com/elastic/kibana/pull/149976#issuecomment-1426083304 The 'Learn more' link leads to [the Ingest API documentation for Metadata](https://www.elastic.co/guide/en/elasticsearch/reference/8.6/put-pipeline-api.html#pipeline-metadata). meta ### Checklist Delete any items that are not applicable to this PR. - [X] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) cc: @gchaps --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/pipeline_form/pipeline_form_fields.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx index ce9a05f666be0..0c8177715c10f 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx @@ -139,7 +139,7 @@ export const PipelineFormFields: React.FunctionComponent = ({ <> From 858eec506cff891e3919ec15f701ef4d8c814a5e Mon Sep 17 00:00:00 2001 From: Yan Savitski Date: Wed, 15 Feb 2023 14:43:41 +0100 Subject: [PATCH 14/68] Behavioral analytics view in discover (#151157) Issue [[Behavioral Analytics] View collection events in Discover image --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../common/types/analytics.ts | 4 + .../__mocks__/kea_logic/kibana_logic.mock.ts | 7 +- ...ytics_collection_data_view_id_api_logic.ts | 31 +++++ ...tics_collection_data_view_id_logic.test.ts | 46 +++++++ ...analytics_collection_data_view_id_logic.ts | 53 +++++++ .../analytics_collection_view.test.tsx | 31 ++++- .../analytics_collection_view.tsx | 42 +++++- .../public/applications/index.tsx | 1 + .../shared/kibana/kibana_logic.ts | 2 + ..._analytics_collection_data_view_id.test.ts | 84 ++++++++++++ ...fetch_analytics_collection_data_view_id.ts | 31 +++++ .../enterprise_search/analytics.test.ts | 129 ++++++++++++++---- .../routes/enterprise_search/analytics.ts | 37 +++++ 13 files changed, 467 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/api/fetch_analytics_collection_data_view_id/fetch_analytics_collection_data_view_id_api_logic.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.ts create mode 100644 x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.test.ts create mode 100644 x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.ts diff --git a/x-pack/plugins/enterprise_search/common/types/analytics.ts b/x-pack/plugins/enterprise_search/common/types/analytics.ts index 49c00830a7fc9..3c716a545a9c5 100644 --- a/x-pack/plugins/enterprise_search/common/types/analytics.ts +++ b/x-pack/plugins/enterprise_search/common/types/analytics.ts @@ -17,3 +17,7 @@ export type AnalyticsCollectionDocument = Omit; export interface AnalyticsEventsIndexExists { exists: boolean; } + +export interface AnalyticsCollectionDataViewId { + data_view_id: string | null; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts index 4689317cfd61a..ff4760c11f5c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts @@ -8,13 +8,18 @@ import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; -import { Capabilities } from '@kbn/core/public'; +import { ApplicationStart, Capabilities } from '@kbn/core/public'; import { securityMock } from '@kbn/security-plugin/public/mocks'; import { mockHistory } from '../react_router/state.mock'; export const mockKibanaValues = { + application: { + getUrlForApp: jest.fn( + (appId: string, options?: { path?: string }) => `/app/${appId}${options?.path}` + ), + } as unknown as ApplicationStart, capabilities: {} as Capabilities, config: { host: 'http://localhost:3002' }, charts: chartPluginMock.createStartContract(), diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/api/fetch_analytics_collection_data_view_id/fetch_analytics_collection_data_view_id_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/api/fetch_analytics_collection_data_view_id/fetch_analytics_collection_data_view_id_api_logic.ts new file mode 100644 index 0000000000000..6b4853f2aa581 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/api/fetch_analytics_collection_data_view_id/fetch_analytics_collection_data_view_id_api_logic.ts @@ -0,0 +1,31 @@ +/* + * 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 { AnalyticsCollectionDataViewId } from '../../../../../common/types/analytics'; +import { createApiLogic } from '../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../shared/http'; + +export interface FetchAnalyticsCollectionDataViewIdAPILogicArgs { + id: string; +} + +export type FetchAnalyticsCollectionDataViewIdApiLogicResponse = AnalyticsCollectionDataViewId; + +export const fetchAnalyticsCollectionDataViewId = async ({ + id, +}: FetchAnalyticsCollectionDataViewIdAPILogicArgs): Promise => { + const { http } = HttpLogic.values; + const route = `/internal/enterprise_search/analytics/collections/${id}/data_view_id`; + const response = await http.get(route); + + return response; +}; + +export const FetchAnalyticsCollectionDataViewIdAPILogic = createApiLogic( + ['analytics', 'analytics_collection_data_view_id_api_logic'], + fetchAnalyticsCollectionDataViewId +); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.test.ts new file mode 100644 index 0000000000000..52d0dc2efc7f3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.test.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. + */ + +import { LogicMounter } from '../../../__mocks__/kea_logic'; + +import { Status } from '../../../../../common/types/api'; + +import { AnalyticsCollectionDataViewIdLogic } from './analytics_collection_data_view_id_logic'; + +describe('analyticsCollectionDataViewIdLogic', () => { + const { mount } = new LogicMounter(AnalyticsCollectionDataViewIdLogic); + const dataViewIdMock = '0c3edf-0c3edf-0c3edf-0c3edf-0c3edf'; + + beforeEach(() => { + jest.clearAllMocks(); + jest.useRealTimers(); + mount(); + }); + + const DEFAULT_VALUES = { + data: undefined, + dataViewId: null, + status: Status.IDLE, + }; + + it('has expected default values', () => { + expect(AnalyticsCollectionDataViewIdLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('selectors', () => { + it('updates when apiSuccess listener triggered', () => { + AnalyticsCollectionDataViewIdLogic.actions.apiSuccess({ data_view_id: dataViewIdMock }); + + expect(AnalyticsCollectionDataViewIdLogic.values).toEqual({ + ...DEFAULT_VALUES, + data: { data_view_id: dataViewIdMock }, + dataViewId: dataViewIdMock, + status: Status.SUCCESS, + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.ts new file mode 100644 index 0000000000000..147f43c53e03e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_data_view_id_logic.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { Status } from '../../../../../common/types/api'; + +import { Actions } from '../../../shared/api_logic/create_api_logic'; +import { + FetchAnalyticsCollectionDataViewIdAPILogic, + FetchAnalyticsCollectionDataViewIdApiLogicResponse, +} from '../../api/fetch_analytics_collection_data_view_id/fetch_analytics_collection_data_view_id_api_logic'; + +export interface AnalyticsCollectionDataViewIdActions { + apiSuccess: Actions<{}, FetchAnalyticsCollectionDataViewIdApiLogicResponse>['apiSuccess']; + fetchAnalyticsCollectionDataViewId(id: string): { id: string }; + makeRequest: Actions<{}, FetchAnalyticsCollectionDataViewIdApiLogicResponse>['makeRequest']; +} +export interface AnalyticsCollectionDataViewIdValues { + data: typeof FetchAnalyticsCollectionDataViewIdAPILogic.values.data; + + dataViewId: string | null; + + status: Status; +} + +export const AnalyticsCollectionDataViewIdLogic = kea< + MakeLogicType +>({ + actions: { + fetchAnalyticsCollectionDataViewId: (id) => ({ id }), + }, + connect: { + actions: [ + FetchAnalyticsCollectionDataViewIdAPILogic, + ['makeRequest', 'apiSuccess', 'apiError'], + ], + values: [FetchAnalyticsCollectionDataViewIdAPILogic, ['status', 'data']], + }, + listeners: ({ actions }) => ({ + fetchAnalyticsCollectionDataViewId: ({ id }) => { + actions.makeRequest({ id }); + }, + }), + path: ['enterprise_search', 'analytics', 'collection_data_view_id'], + selectors: ({ selectors }) => ({ + dataViewId: [() => [selectors.data], (data) => data?.data_view_id || null], + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.test.tsx index ebbd898f08641..4fe1828378736 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.test.tsx @@ -10,11 +10,12 @@ import '../../../__mocks__/shallow_useeffect.mock'; import { setMockValues, setMockActions } from '../../../__mocks__/kea_logic'; import { mockUseParams } from '../../../__mocks__/react_router'; -import React from 'react'; +import React, { ReactElement } from 'react'; import { shallow } from 'enzyme'; import { AnalyticsCollection } from '../../../../../common/types/analytics'; +import { EnterpriseSearchAnalyticsPageTemplate } from '../layout/page_template'; import { AnalyticsCollectionIntegrate } from './analytics_collection_integrate/analytics_collection_integrate'; import { AnalyticsCollectionSettings } from './analytics_collection_settings'; @@ -27,10 +28,12 @@ const mockValues = { id: '1', name: 'Analytics Collection 1', } as AnalyticsCollection, + dataViewId: '1234-1234-1234', }; const mockActions = { fetchAnalyticsCollection: jest.fn(), + fetchAnalyticsCollectionDataViewId: jest.fn(), }; describe('AnalyticsOverview', () => { @@ -73,5 +76,31 @@ describe('AnalyticsOverview', () => { expect(wrapper.prop('pageViewTelemetry')).toBe('View Analytics Collection - settings'); }); + + it('send correct pageHeader rightSideItems when dataViewId exists', async () => { + setMockValues(mockValues); + setMockActions(mockActions); + + const rightSideItems = shallow() + ?.find(EnterpriseSearchAnalyticsPageTemplate) + ?.prop('pageHeader')?.rightSideItems; + + expect(rightSideItems).toHaveLength(1); + + expect((rightSideItems?.[0] as ReactElement).props?.children?.props?.href).toBe( + "/app/discover#/?_a=(index:'1234-1234-1234')" + ); + }); + + it('hide pageHeader rightSideItems when dataViewId not exists', async () => { + setMockValues({ ...mockValues, dataViewId: null }); + setMockActions(mockActions); + + const wrapper = shallow(); + + expect( + wrapper?.find(EnterpriseSearchAnalyticsPageTemplate)?.prop('pageHeader')?.rightSideItems + ).toBeUndefined(); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx index ae5d3e4166224..17e89d97195a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx @@ -10,8 +10,19 @@ import { useParams } from 'react-router-dom'; import { useActions, useValues } from 'kea'; -import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiIconTip, + EuiLink, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { RedirectAppLinks } from '@kbn/kibana-react-plugin/public'; import { generateEncodedPath } from '../../../shared/encode_path_params'; import { KibanaLogic } from '../../../shared/kibana'; @@ -20,6 +31,8 @@ import { COLLECTION_CREATION_PATH, COLLECTION_VIEW_PATH } from '../../routes'; import { EnterpriseSearchAnalyticsPageTemplate } from '../layout/page_template'; +import { AnalyticsCollectionDataViewIdLogic } from './analytics_collection_data_view_id_logic'; + import { AnalyticsCollectionEvents } from './analytics_collection_events'; import { AnalyticsCollectionIntegrate } from './analytics_collection_integrate/analytics_collection_integrate'; import { AnalyticsCollectionSettings } from './analytics_collection_settings'; @@ -34,9 +47,11 @@ export const collectionViewBreadcrumbs = [ export const AnalyticsCollectionView: React.FC = () => { const { fetchAnalyticsCollection } = useActions(FetchAnalyticsCollectionLogic); + const { fetchAnalyticsCollectionDataViewId } = useActions(AnalyticsCollectionDataViewIdLogic); const { analyticsCollection, isLoading } = useValues(FetchAnalyticsCollectionLogic); + const { dataViewId } = useValues(AnalyticsCollectionDataViewIdLogic); const { id, section } = useParams<{ id: string; section: string }>(); - const { navigateToUrl } = useValues(KibanaLogic); + const { navigateToUrl, application } = useValues(KibanaLogic); const collectionViewTabs = [ { id: 'events', @@ -84,6 +99,7 @@ export const AnalyticsCollectionView: React.FC = () => { useEffect(() => { fetchAnalyticsCollection(id); + fetchAnalyticsCollectionDataViewId(id); }, []); return ( @@ -101,6 +117,28 @@ export const AnalyticsCollectionView: React.FC = () => { } ), pageTitle: analyticsCollection?.name, + rightSideItems: dataViewId + ? [ + + + + } + type="inspect" + /> + + , + ] + : undefined, tabs: [...collectionViewTabs], }} > diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 2b25fabe056f0..11250d05a845e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -57,6 +57,7 @@ export const renderApp = ( const store = getContext().store; const unmountKibanaLogic = mountKibanaLogic({ + application: core.application, capabilities: core.application.capabilities, config, productAccess, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index db80e75fbcf20..070c273884772 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -30,6 +30,7 @@ type RequiredFieldsOnly = { [K in keyof T as T[K] extends Required[K] ? K : never]: T[K]; }; interface KibanaLogicProps { + application: ApplicationStart; config: { host?: string }; productAccess: ProductAccess; // Kibana core @@ -57,6 +58,7 @@ export interface KibanaValues extends Omit { export const KibanaLogic = kea>({ path: ['enterprise_search', 'kibana_logic'], reducers: ({ props }) => ({ + application: [props.application || {}, {}], capabilities: [props.capabilities || {}, {}], config: [props.config || {}, {}], charts: [props.charts, {}], diff --git a/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.test.ts b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.test.ts new file mode 100644 index 0000000000000..1db6ccedfd920 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.test.ts @@ -0,0 +1,84 @@ +/* + * 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 { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; + +import { DataViewsService } from '@kbn/data-views-plugin/common'; + +import { ErrorCode } from '../../../common/types/error_codes'; + +import { fetchAnalyticsCollectionById } from './fetch_analytics_collection'; +import { fetchAnalyticsCollectionDataViewId } from './fetch_analytics_collection_data_view_id'; + +jest.mock('./fetch_analytics_collection', () => ({ + fetchAnalyticsCollectionById: jest.fn(), +})); + +describe('fetch analytics collection data view id', () => { + const mockClient = {}; + const dataViewService = { find: jest.fn() }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return data view id of analytics collection by Id', async () => { + const mockCollectionId = 'collectionId'; + const mockDataViewId = 'dataViewId'; + const mockCollection = { events_datastream: 'log-collection-data-stream' }; + (fetchAnalyticsCollectionById as jest.Mock).mockImplementationOnce(() => + Promise.resolve(mockCollection) + ); + + dataViewService.find.mockImplementationOnce(() => Promise.resolve([{ id: mockDataViewId }])); + + await expect( + fetchAnalyticsCollectionDataViewId( + mockClient as unknown as IScopedClusterClient, + dataViewService as unknown as DataViewsService, + mockCollectionId + ) + ).resolves.toEqual({ data_view_id: mockDataViewId }); + expect(fetchAnalyticsCollectionById).toHaveBeenCalledWith(mockClient, mockCollectionId); + expect(dataViewService.find).toHaveBeenCalledWith(mockCollection.events_datastream, 1); + }); + + it('should return null when data view not found', async () => { + const mockCollectionId = 'collectionId'; + const mockCollection = { events_datastream: 'log-collection-data-stream' }; + (fetchAnalyticsCollectionById as jest.Mock).mockImplementationOnce(() => + Promise.resolve(mockCollection) + ); + + dataViewService.find.mockImplementationOnce(() => Promise.resolve([])); + + await expect( + fetchAnalyticsCollectionDataViewId( + mockClient as unknown as IScopedClusterClient, + dataViewService as unknown as DataViewsService, + mockCollectionId + ) + ).resolves.toEqual({ data_view_id: null }); + expect(fetchAnalyticsCollectionById).toHaveBeenCalledWith(mockClient, mockCollectionId); + expect(dataViewService.find).toHaveBeenCalledWith(mockCollection.events_datastream, 1); + }); + + it('should throw an error when analytics collection not found', async () => { + const mockCollectionId = 'collectionId'; + (fetchAnalyticsCollectionById as jest.Mock).mockImplementationOnce(() => Promise.resolve(null)); + + await expect( + fetchAnalyticsCollectionDataViewId( + mockClient as unknown as IScopedClusterClient, + dataViewService as unknown as DataViewsService, + mockCollectionId + ) + ).rejects.toThrowError(ErrorCode.ANALYTICS_COLLECTION_NOT_FOUND); + expect(fetchAnalyticsCollectionById).toHaveBeenCalledWith(mockClient, mockCollectionId); + expect(dataViewService.find).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.ts b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.ts new file mode 100644 index 0000000000000..0ec07139d7b41 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection_data_view_id.ts @@ -0,0 +1,31 @@ +/* + * 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 { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; +import { DataViewsService } from '@kbn/data-views-plugin/common'; + +import { AnalyticsCollectionDataViewId } from '../../../common/types/analytics'; + +import { ErrorCode } from '../../../common/types/error_codes'; + +import { fetchAnalyticsCollectionById } from './fetch_analytics_collection'; + +export const fetchAnalyticsCollectionDataViewId = async ( + elasticsearchClient: IScopedClusterClient, + dataViewsService: DataViewsService, + collectionId: string +): Promise => { + const collection = await fetchAnalyticsCollectionById(elasticsearchClient, collectionId); + + if (!collection) { + throw new Error(ErrorCode.ANALYTICS_COLLECTION_NOT_FOUND); + } + + const collectionDataView = await dataViewsService.find(collection.events_datastream, 1); + + return { data_view_id: collectionDataView?.[0]?.id || null }; +}; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts index 09195df9768dc..f216a23e58e0a 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.test.ts @@ -14,8 +14,18 @@ import { DataPluginStart } from '@kbn/data-plugin/server/plugin'; jest.mock('../../lib/analytics/fetch_analytics_collection', () => ({ fetchAnalyticsCollectionById: jest.fn(), })); -import { AnalyticsCollection } from '../../../common/types/analytics'; + +jest.mock('../../lib/analytics/fetch_analytics_collection_data_view_id', () => ({ + fetchAnalyticsCollectionDataViewId: jest.fn(), +})); + +import { + AnalyticsCollection, + AnalyticsCollectionDataViewId, +} from '../../../common/types/analytics'; +import { ErrorCode } from '../../../common/types/error_codes'; import { fetchAnalyticsCollectionById } from '../../lib/analytics/fetch_analytics_collection'; +import { fetchAnalyticsCollectionDataViewId } from '../../lib/analytics/fetch_analytics_collection_data_view_id'; import { registerAnalyticsRoutes } from './analytics'; @@ -23,36 +33,36 @@ describe('Enterprise Search Analytics API', () => { let mockRouter: MockRouter; const mockClient = {}; - beforeEach(() => { - const context = { - core: Promise.resolve({ elasticsearch: { client: mockClient } }), - } as jest.Mocked; + describe('GET /internal/enterprise_search/analytics/collections/{id}', () => { + beforeEach(() => { + const context = { + core: Promise.resolve({ elasticsearch: { client: mockClient } }), + } as jest.Mocked; - mockRouter = new MockRouter({ - context, - method: 'get', - path: '/internal/enterprise_search/analytics/collections/{id}', - }); + mockRouter = new MockRouter({ + context, + method: 'get', + path: '/internal/enterprise_search/analytics/collections/{id}', + }); - const mockDataPlugin = { - indexPatterns: { - dataViewsServiceFactory: jest.fn(), - }, - }; - - const mockedSavedObjects = { - getScopedClient: jest.fn(), - }; - - registerAnalyticsRoutes({ - ...mockDependencies, - data: mockDataPlugin as unknown as DataPluginStart, - savedObjects: mockedSavedObjects as unknown as SavedObjectsServiceStart, - router: mockRouter.router, + const mockDataPlugin = { + indexPatterns: { + dataViewsServiceFactory: jest.fn(), + }, + }; + + const mockedSavedObjects = { + getScopedClient: jest.fn(), + }; + + registerAnalyticsRoutes({ + ...mockDependencies, + data: mockDataPlugin as unknown as DataPluginStart, + savedObjects: mockedSavedObjects as unknown as SavedObjectsServiceStart, + router: mockRouter.router, + }); }); - }); - describe('GET /internal/enterprise_search/analytics/collections/{id}', () => { it('fetches a defined analytics collection name', async () => { const mockData: AnalyticsCollection = { event_retention_day_length: 30, @@ -90,4 +100,69 @@ describe('Enterprise Search Analytics API', () => { }); }); }); + + describe('GET /internal/enterprise_search/analytics/collections/{id}/data_view_id', () => { + beforeEach(() => { + const context = { + core: Promise.resolve({ elasticsearch: { client: mockClient } }), + } as jest.Mocked; + + mockRouter = new MockRouter({ + context, + method: 'get', + path: '/internal/enterprise_search/analytics/collections/{id}/data_view_id', + }); + + const mockDataPlugin = { + indexPatterns: { + dataViewsServiceFactory: jest.fn(), + }, + }; + + const mockedSavedObjects = { + getScopedClient: jest.fn(), + }; + + registerAnalyticsRoutes({ + ...mockDependencies, + data: mockDataPlugin as unknown as DataPluginStart, + savedObjects: mockedSavedObjects as unknown as SavedObjectsServiceStart, + router: mockRouter.router, + }); + }); + + it('fetches a defined data view id by collection id', async () => { + const mockData: AnalyticsCollectionDataViewId = { + data_view_id: '03fca-1234-5678-9abc-1234', + }; + + (fetchAnalyticsCollectionDataViewId as jest.Mock).mockImplementationOnce(() => { + return Promise.resolve(mockData); + }); + await mockRouter.callRoute({ params: { id: '1' } }); + + expect(mockRouter.response.ok).toHaveBeenCalledWith({ + body: mockData, + }); + }); + + it('throws a 404 error if collection not found by id', async () => { + (fetchAnalyticsCollectionDataViewId as jest.Mock).mockImplementationOnce(() => { + throw new Error(ErrorCode.ANALYTICS_COLLECTION_NOT_FOUND); + }); + await mockRouter.callRoute({ + params: { id: '1' }, + }); + + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + body: { + attributes: { + error_code: 'analytics_collection_not_found', + }, + message: 'Analytics collection not found', + }, + statusCode: 404, + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts index 1e84a1b81845e..9306a1958956a 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts @@ -20,6 +20,7 @@ import { fetchAnalyticsCollectionById, fetchAnalyticsCollections, } from '../../lib/analytics/fetch_analytics_collection'; +import { fetchAnalyticsCollectionDataViewId } from '../../lib/analytics/fetch_analytics_collection_data_view_id'; import { RouteDependencies } from '../../plugin'; import { createError } from '../../utils/create_error'; import { elasticsearchErrorHandler } from '../../utils/elasticsearch_error_handler'; @@ -180,4 +181,40 @@ export function registerAnalyticsRoutes({ return response.ok({ body: { exists: true } }); }) ); + + router.get( + { + path: '/internal/enterprise_search/analytics/collections/{id}/data_view_id', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + const core = await context.core; + const elasticsearchClient = core.elasticsearch.client; + const dataViewsService = await data.indexPatterns.dataViewsServiceFactory( + savedObjects.getScopedClient(request), + elasticsearchClient.asCurrentUser, + request + ); + + try { + const dataViewId = await fetchAnalyticsCollectionDataViewId( + elasticsearchClient, + dataViewsService, + request.params.id + ); + + return response.ok({ body: dataViewId }); + } catch (error) { + if ((error as Error).message === ErrorCode.ANALYTICS_COLLECTION_NOT_FOUND) { + return createIndexNotFoundError(error, response); + } + + throw error; + } + }) + ); } From c6dc97a80a8176fdad81080c71a2104e2c9995fc Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 Feb 2023 13:51:47 +0000 Subject: [PATCH 15/68] [ML] Fix module setup apply to all spaces (#151270) The shared module setup function is not passing across the `applyToAllSpaces` parameter. This only affects plugins calling `setup` via the server side shared function, not the ML plugin itself or any plugins calling the kibana endpoint. --- x-pack/plugins/ml/server/shared_services/providers/modules.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/server/shared_services/providers/modules.ts b/x-pack/plugins/ml/server/shared_services/providers/modules.ts index 7180e0c0094e6..a9672b2ffdc67 100644 --- a/x-pack/plugins/ml/server/shared_services/providers/modules.ts +++ b/x-pack/plugins/ml/server/shared_services/providers/modules.ts @@ -112,7 +112,8 @@ export function getModulesProvider( payload.end, payload.jobOverrides, payload.datafeedOverrides, - payload.estimateModelMemory + payload.estimateModelMemory, + payload.applyToAllSpaces ); }); }, From d04dc43513d747a8f64ea42168e5537486b95ef3 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 Feb 2023 13:52:12 +0000 Subject: [PATCH 16/68] [ML] AIOPs: Improve log categorization discover filter label (#151036) Fixes the Discover filters generated by the Log Categorisation page, adding an alias so the whole query isn't displayed. Label is `Categorization - ` Before: ![image](https://user-images.githubusercontent.com/22172091/218516903-2831e416-1b7f-42ee-b296-601d4bb99825.png) After: ![image](https://user-images.githubusercontent.com/22172091/218516727-a7327a20-332f-45bd-907d-40595dfec945.png) --- .../log_categorization/use_discover_links.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts index a56d5c0cdfc6e..2bc63415f9b95 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/use_discover_links.ts @@ -9,6 +9,7 @@ import rison from '@kbn/rison'; import moment from 'moment'; import type { TimeRangeBounds } from '@kbn/data-plugin/common'; +import { i18n } from '@kbn/i18n'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import type { Category } from './use_categorize_request'; import type { QueryMode } from './category_table'; @@ -55,6 +56,16 @@ export function useDiscoverLinks() { })), }, }, + meta: { + alias: i18n.translate('xpack.aiops.logCategorization.filterAliasLabel', { + defaultMessage: 'Categorization - {field}', + values: { + field, + }, + }), + index, + disabled: false, + }, }, ], index, From cab487d7c8fa8c12373e11efa6281dff84b9c1f8 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau Date: Wed, 15 Feb 2023 09:00:00 -0500 Subject: [PATCH 17/68] [RAM] Uptime allow rac api (#151207) ## Summary Allow `rac` api for uptime privileges and to avoid that image --- x-pack/plugins/synthetics/server/feature.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/synthetics/server/feature.ts b/x-pack/plugins/synthetics/server/feature.ts index f68b97668841d..4026bbdb19fb9 100644 --- a/x-pack/plugins/synthetics/server/feature.ts +++ b/x-pack/plugins/synthetics/server/feature.ts @@ -30,7 +30,7 @@ export const uptimeFeature = { all: { app: ['uptime', 'kibana', 'synthetics'], catalogue: ['uptime'], - api: ['uptime-read', 'uptime-write', 'lists-all'], + api: ['uptime-read', 'uptime-write', 'lists-all', 'rac'], savedObject: { all: [ umDynamicSettings.name, From 5fa79fc9b364058464603f527eb30ab1f5d376b4 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 15 Feb 2023 15:03:37 +0100 Subject: [PATCH 18/68] Remove @loaders.gl/polyfills dev-dependency (#151287) --- package.json | 2 - renovate.json | 1 - .../geojson_importer/geojson_importer.test.js | 1 - yarn.lock | 230 +----------------- 4 files changed, 9 insertions(+), 225 deletions(-) diff --git a/package.json b/package.json index bbd67fbd89633..66dd8cb08e49f 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ "**/hoist-non-react-statics": "^3.3.2", "**/isomorphic-fetch/node-fetch": "^2.6.7", "**/istanbul-lib-coverage": "^3.2.0", - "**/json-schema": "^0.4.0", "**/minimatch": "^3.1.2", "**/minimist": "^1.2.6", "**/pdfkit/crypto-js": "4.0.0", @@ -1108,7 +1107,6 @@ "@kbn/web-worker-stub": "link:packages/kbn-web-worker-stub", "@kbn/whereis-pkg-cli": "link:packages/kbn-whereis-pkg-cli", "@kbn/yarn-lock-validator": "link:packages/kbn-yarn-lock-validator", - "@loaders.gl/polyfills": "^2.3.5", "@mapbox/vector-tile": "1.3.1", "@octokit/rest": "^16.35.0", "@openpgp/web-stream-tools": "^0.0.10", diff --git a/renovate.json b/renovate.json index ce12b9dd5a248..c72e955dde18d 100644 --- a/renovate.json +++ b/renovate.json @@ -88,7 +88,6 @@ "groupName": "polyfills", "matchPackageNames": ["core-js"], "matchPackagePatterns": ["polyfill"], - "excludePackageNames": ["@loaders.gl/polyfills"], "reviewers": ["team:kibana-operations"], "matchBaseBranches": ["main"], "labels": ["Team:Operations", "release_note:skip"], diff --git a/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.test.js b/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.test.js index 7b621c4ccbcad..eb6594422fb10 100644 --- a/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.test.js +++ b/x-pack/plugins/file_upload/public/importer/geo/geojson_importer/geojson_importer.test.js @@ -6,7 +6,6 @@ */ import { GeoJsonImporter } from './geojson_importer'; -import '@loaders.gl/polyfills'; const FEATURE_COLLECTION = { type: 'FeatureCollection', diff --git a/yarn.lock b/yarn.lock index 99e9088e676e4..188c3c05eec83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5571,19 +5571,6 @@ "@babel/runtime" "^7.3.1" "@probe.gl/stats" "^3.3.0" -"@loaders.gl/polyfills@^2.3.5": - version "2.3.5" - resolved "https://registry.yarnpkg.com/@loaders.gl/polyfills/-/polyfills-2.3.5.tgz#5f32b732c8f2a7e80c221f19ee2d01725f09b5c3" - integrity sha512-sSoeqtH2UJ1eqyM18AnYppLb5dapvWxt4hEv7xXmhXfVXxjBzUonLEg8d3UxfGEsxtxVmbS5DXHyL+uhYcLarw== - dependencies: - "@babel/runtime" "^7.3.1" - get-pixels "^3.3.2" - ndarray "^1.0.18" - save-pixels "^2.3.2" - stream-to-async-iterator "^0.2.0" - through "^2.3.8" - web-streams-polyfill "^3.0.0" - "@loaders.gl/shapefile@^2.3.1": version "2.3.13" resolved "https://registry.yarnpkg.com/@loaders.gl/shapefile/-/shapefile-2.3.13.tgz#5c6c9cc4a113c2f6739c0eb553ae9ceb9d1840ae" @@ -9960,7 +9947,7 @@ ajv-keywords@^5.0.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -12371,13 +12358,6 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -contentstream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/contentstream/-/contentstream-1.0.0.tgz#0bdcfa46da30464a86ce8fa7ece565410dc6f9a5" - integrity sha1-C9z6RtowRkqGzo+n7OVlQQ3G+aU= - dependencies: - readable-stream "~1.0.33-1" - convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -12900,13 +12880,6 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -cwise-compiler@^1.0.0, cwise-compiler@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/cwise-compiler/-/cwise-compiler-1.1.3.tgz#f4d667410e850d3a313a7d2db7b1e505bb034cc5" - integrity sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU= - dependencies: - uniq "^1.0.0" - cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -13390,11 +13363,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz#18ae979a6a0ca994b0625853916d2662bbae0b1a" - integrity sha1-GK6XmmoMqZSwYlhTkW0mYruuCxo= - data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -16302,23 +16270,6 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-pixels@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/get-pixels/-/get-pixels-3.3.2.tgz#3f62fb8811932c69f262bba07cba72b692b4ff03" - integrity sha512-6ar+8yPxRd1pskEcl2GSEu1La0+xYRjjnkby6AYiRDDwZ0tJbPQmHnSeH9fGLskT8kvR0OukVgtZLcsENF9YKQ== - dependencies: - data-uri-to-buffer "0.0.3" - jpeg-js "^0.3.2" - mime-types "^2.0.1" - ndarray "^1.0.13" - ndarray-pack "^1.1.1" - node-bitmap "0.0.1" - omggif "^1.0.5" - parse-data-uri "^0.2.0" - pngjs "^3.3.3" - request "^2.44.0" - through "^2.3.4" - get-port@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" @@ -16390,13 +16341,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gif-encoder@~0.4.1: - version "0.4.3" - resolved "https://registry.yarnpkg.com/gif-encoder/-/gif-encoder-0.4.3.tgz#8a2b4fe8ca895a48e3a0b6cbb340a0a6a3571899" - integrity sha1-iitP6MqJWkjjoLbLs0CgpqNXGJk= - dependencies: - readable-stream "~1.1.9" - git-hooks-list@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-1.0.3.tgz#be5baaf78203ce342f2f844a9d2b03dba1b45156" @@ -16803,19 +16747,6 @@ handlebars@4.7.7, handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -17367,15 +17298,6 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - http-signature@~1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" @@ -17715,11 +17637,6 @@ io-ts@^2.0.5: resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.0.5.tgz#e6e3db9df8b047f9cbd6b69e7d2ad3e6437a0b13" integrity sha512-pL7uUptryanI5Glv+GUv7xh+aLBjxGEDmLwmEYNSx0yOD3djK0Nw5Bt0N6BAkv9LadOUU7QKpRsLcqnTh3UlLA== -iota-array@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/iota-array/-/iota-array-1.0.0.tgz#81ef57fe5d05814cd58c2483632a99c30a0e8087" - integrity sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc= - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -17829,7 +17746,7 @@ is-boolean-object@^1.0.1, is-boolean-object@^1.1.0: dependencies: call-bind "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.1: +is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -19252,16 +19169,6 @@ joi@^17.3.0, joi@^17.7.1: "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" -jpeg-js@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.0.4.tgz#06aaf47efec7af0b1924a59cd695a6d2b5ed870e" - integrity sha1-Bqr0fv7HrwsZJKWc1pWm0rXthw4= - -jpeg-js@^0.3.2: - version "0.3.7" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.7.tgz#471a89d06011640592d314158608690172b1028d" - integrity sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ== - jquery@^3.5.0: version "3.6.0" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" @@ -19443,7 +19350,7 @@ json-schema-typed@^8.0.1: resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-8.0.1.tgz#826ee39e3b6cef536f85412ff048d3ff6f19dfa0" integrity sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg== -json-schema@0.2.3, json-schema@0.4.0, json-schema@^0.4.0: +json-schema@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== @@ -19538,16 +19445,6 @@ jsonwebtoken@^9.0.0: ms "^2.1.1" semver "^7.3.8" -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - jsprim@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" @@ -20928,7 +20825,7 @@ mime-db@1.51.0, mime-db@1.x.x, "mime-db@>= 1.40.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== -mime-types@^2.0.1, mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.34" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== @@ -21468,29 +21365,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -ndarray-ops@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/ndarray-ops/-/ndarray-ops-1.2.2.tgz#59e88d2c32a7eebcb1bc690fae141579557a614e" - integrity sha1-WeiNLDKn7ryxvGkPrhQVeVV6YU4= - dependencies: - cwise-compiler "^1.0.0" - -ndarray-pack@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ndarray-pack/-/ndarray-pack-1.2.1.tgz#8caebeaaa24d5ecf70ff86020637977da8ee585a" - integrity sha1-jK6+qqJNXs9w/4YCBjeXfajuWFo= - dependencies: - cwise-compiler "^1.1.2" - ndarray "^1.0.13" - -ndarray@^1.0.13, ndarray@^1.0.18: - version "1.0.19" - resolved "https://registry.yarnpkg.com/ndarray/-/ndarray-1.0.19.tgz#6785b5f5dfa58b83e31ae5b2a058cfd1ab3f694e" - integrity sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ== - dependencies: - iota-array "^1.0.0" - is-buffer "^1.0.2" - nearley@^2.7.10: version "2.16.0" resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.16.0.tgz#77c297d041941d268290ec84b739d0ee297e83a7" @@ -21591,11 +21465,6 @@ node-addon-api@^5.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA== -node-bitmap@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/node-bitmap/-/node-bitmap-0.0.1.tgz#180eac7003e0c707618ef31368f62f84b2a69091" - integrity sha1-GA6scAPgxwdhjvMTaPYvhLKmkJE= - node-cache@^5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" @@ -21931,11 +21800,6 @@ nyc@15.1.0, nyc@^15.1.0: test-exclude "^6.0.0" yargs "^15.0.2" -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - object-assign@4.X, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -22087,11 +21951,6 @@ octokit-pagination-methods@^1.1.0: resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== -omggif@^1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" - integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -22502,13 +22361,6 @@ parse-asn1@^5.0.0: evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" -parse-data-uri@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/parse-data-uri/-/parse-data-uri-0.2.0.tgz#bf04d851dd5c87b0ab238e5d01ace494b604b4c9" - integrity sha1-vwTYUd1ch7CrI45dAazklLYEtMk= - dependencies: - data-uri-to-buffer "0.0.3" - parse-entities@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" @@ -22926,12 +22778,7 @@ png-js@^1.0.0: resolved "https://registry.yarnpkg.com/png-js/-/png-js-1.0.0.tgz#e5484f1e8156996e383aceebb3789fd75df1874d" integrity sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g== -pngjs-nozlib@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pngjs-nozlib/-/pngjs-nozlib-1.0.0.tgz#9e64d602cfe9cce4d9d5997d0687429a73f0b7d7" - integrity sha1-nmTWAs/pzOTZ1Zl9BodCmnPwt9c= - -pngjs@^3.3.3, pngjs@^3.4.0: +pngjs@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== @@ -24592,7 +24439,7 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17, readable-stream@~1.0.27-1, readable-stream@~1.0.33-1: +readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17, readable-stream@~1.0.27-1: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= @@ -24611,16 +24458,6 @@ readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0 string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readdir-glob@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4" @@ -25117,32 +24954,6 @@ request-progress@^3.0.0: dependencies: throttleit "^1.0.0" -request@^2.44.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -25540,19 +25351,6 @@ sass-loader@^10.4.1: schema-utils "^3.0.0" semver "^7.3.2" -save-pixels@^2.3.2: - version "2.3.4" - resolved "https://registry.yarnpkg.com/save-pixels/-/save-pixels-2.3.4.tgz#49d349c06b8d7c0127dbf0da24b44aca5afb59fe" - integrity sha1-SdNJwGuNfAEn2/DaJLRKylr7Wf4= - dependencies: - contentstream "^1.0.0" - gif-encoder "~0.4.1" - jpeg-js "0.0.4" - ndarray "^1.0.18" - ndarray-ops "^1.2.2" - pngjs-nozlib "^1.0.0" - through "^2.3.4" - sax@>=0.6.0, sax@^1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -26398,7 +26196,7 @@ sql-summary@^1.0.1: resolved "https://registry.yarnpkg.com/sql-summary/-/sql-summary-1.0.1.tgz#a2dddb5435bae294eb11424a7330dc5bafe09c2b" integrity sha512-IpCr2tpnNkP3Jera4ncexsZUp0enJBLr+pHCyTweMUBrbJsTgQeLWx1FXLhoBj/MvcnUQpkgOn2EY8FKOkUzww== -sshpk@^1.14.1, sshpk@^1.7.0: +sshpk@^1.14.1: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== @@ -26599,11 +26397,6 @@ stream-slicer@0.0.6: resolved "https://registry.yarnpkg.com/stream-slicer/-/stream-slicer-0.0.6.tgz#f86b2ac5c2440b7a0a87b71f33665c0788046138" integrity sha1-+GsqxcJEC3oKh7cfM2ZcB4gEYTg= -stream-to-async-iterator@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/stream-to-async-iterator/-/stream-to-async-iterator-0.2.0.tgz#bef5c885e9524f98b2fa5effecc357bd58483780" - integrity sha1-vvXIhelST5iy+l7/7MNXvVhIN4A= - streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -27379,7 +27172,7 @@ through2@~0.4.1: readable-stream "~1.0.17" xtend "~2.1.1" -"through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3.4: +"through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -28025,11 +27818,6 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -uniq@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -29042,7 +28830,7 @@ web-namespaces@^1.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== -web-streams-polyfill@^3.0.0, web-streams-polyfill@^3.2.0: +web-streams-polyfill@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== From 24efb8597e8ed598f930373a32238e4b54c7309c Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Wed, 15 Feb 2023 08:23:59 -0600 Subject: [PATCH 19/68] [Lens] better support for user messages on embeddable (#149458) --- ...et_application_user_messages.test.tsx.snap | 3 +- .../get_application_user_messages.tsx | 2 +- .../lens/public/embeddable/embeddable.tsx | 229 +++++++++++++----- .../public/embeddable/expression_wrapper.tsx | 143 ++++------- .../kbn_archiver/lens/missing_fields.json | 2 +- 5 files changed, 211 insertions(+), 168 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/__snapshots__/get_application_user_messages.test.tsx.snap b/x-pack/plugins/lens/public/app_plugin/__snapshots__/get_application_user_messages.test.tsx.snap index 0219edefe4b23..437d331f5bdec 100644 --- a/x-pack/plugins/lens/public/app_plugin/__snapshots__/get_application_user_messages.test.tsx.snap +++ b/x-pack/plugins/lens/public/app_plugin/__snapshots__/get_application_user_messages.test.tsx.snap @@ -77,7 +77,8 @@ Array [ href="fake/url" style={ Object { - "display": "block", + "textAlign": "center", + "width": "100%", } } > diff --git a/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.tsx b/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.tsx index f5190ec334157..b56736eabe7ac 100644 --- a/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.tsx +++ b/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.tsx @@ -153,7 +153,7 @@ function getMissingIndexPatternsErrors( href={core.application.getUrlForApp('management', { path: '/kibana/indexPatterns/create', })} - style={{ display: 'block' }} + style={{ width: '100%', textAlign: 'center' }} data-test-subj="configuration-failure-reconfigure-indexpatterns" > {i18n.translate('xpack.lens.editorFrame.dataViewReconfigure', { diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index e89e8c61e3185..a430f8ab66e1e 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -73,6 +73,8 @@ import { MultiClickTriggerEvent, } from '@kbn/charts-plugin/public'; import { DataViewSpec } from '@kbn/data-views-plugin/common'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n-react'; +import { EuiEmptyPrompt } from '@elastic/eui'; import { useEuiFontSize, useEuiTheme } from '@elastic/eui'; import { getExecutionContextEvents, trackUiCounterEvents } from '../lens_ui_telemetry'; import { Document } from '../persistence'; @@ -96,6 +98,7 @@ import { AddUserMessages, isMessageRemovable, UserMessagesGetter, + UserMessagesDisplayLocationId, } from '../types'; import { getEditPath, DOC_TYPE } from '../../common'; @@ -194,6 +197,52 @@ export interface ViewUnderlyingDataArgs { columns: string[]; } +function VisualizationErrorPanel({ errors, canEdit }: { errors: UserMessage[]; canEdit: boolean }) { + const showMore = errors.length > 1; + const canFixInLens = canEdit && errors.some(({ fixableInEditor }) => fixableInEditor); + return ( +
+ + {errors.length ? ( + <> +

{errors[0].longMessage}

+ {showMore && !canFixInLens ? ( +

+ +

+ ) : null} + {canFixInLens ? ( +

+ +

+ ) : null} + + ) : ( +

+ +

+ )} + + } + /> +
+ ); +} + const getExpressionFromDocument = async ( document: Document, documentToExpression: LensEmbeddableDeps['documentToExpression'] @@ -296,6 +345,27 @@ const EmbeddableMessagesPopover = ({ messages }: { messages: UserMessage[] }) => ); }; +const blockingMessageDisplayLocations: UserMessagesDisplayLocationId[] = [ + 'visualization', + 'visualizationOnEmbeddable', +]; + +const MessagesBadge = ({ onMount }: { onMount: (el: HTMLDivElement) => void }) => ( +
{ + if (el) { + onMount(el); + } + }} + /> +); + export class Embeddable extends AbstractEmbeddable implements @@ -311,7 +381,6 @@ export class Embeddable private savedVis: Document | undefined; private expression: string | undefined | null; private domNode: HTMLElement | Element | undefined; - private badgeDomNode: HTMLElement | Element | undefined; private subscription: Subscription; private isInitialized = false; private inputReloadSubscriptions: Subscription[]; @@ -503,10 +572,6 @@ export class Embeddable ); }; - private get hasAnyErrors() { - return this.getUserMessages(undefined, { severity: 'error' }).length > 0; - } - private _userMessages: UserMessage[] = []; // loads all available user messages @@ -583,7 +648,7 @@ export class Embeddable if (addedMessageIds.length) { this.additionalUserMessages = newMessageMap; - this.renderBadgeMessages(); + this.renderUserMessages(); } return () => { @@ -837,22 +902,22 @@ export class Embeddable this.domNode.setAttribute('data-shared-item', ''); - const errors = this.getUserMessages(['visualization', 'visualizationOnEmbeddable'], { + const blockingErrors = this.getUserMessages(blockingMessageDisplayLocations, { severity: 'error', }); this.updateOutput({ loading: true, - error: errors.length + error: blockingErrors.length ? new Error( - typeof errors[0].longMessage === 'string' - ? errors[0].longMessage - : errors[0].shortMessage + typeof blockingErrors[0].longMessage === 'string' + ? blockingErrors[0].longMessage + : blockingErrors[0].shortMessage ) : undefined, }); - if (errors.length) { + if (blockingErrors.length) { this.renderComplete.dispatchError(); } else { this.renderComplete.dispatchInProgress(); @@ -860,59 +925,94 @@ export class Embeddable const input = this.getInput(); - render( - - { - this.updateOutput({ error: new Error(message) }); - this.logError('runtime'); - }} - noPadding={this.visDisplayOptions.noPadding} - /> -
{ - if (el) { + if (this.expression && !blockingErrors.length) { + render( + <> + + this.addUserMessages(messages)} + onRuntimeError={(message) => { + this.updateOutput({ error: new Error(message) }); + this.logError('runtime'); + }} + noPadding={this.visDisplayOptions.noPadding} + /> + + { this.badgeDomNode = el; this.renderBadgeMessages(); - } - }} - /> - , - domNode - ); + }} + /> + , + domNode + ); + } + + this.renderUserMessages(); + } + + private renderUserMessages() { + const errors = this.getUserMessages(['visualization', 'visualizationOnEmbeddable'], { + severity: 'error', + }); + + if (errors.length && this.domNode) { + render( + <> + + + + + + { + this.badgeDomNode = el; + this.renderBadgeMessages(); + }} + /> + , + this.domNode + ); + } + + this.renderBadgeMessages(); } - private renderBadgeMessages() { + badgeDomNode?: HTMLDivElement; + + /** + * This method is called on every render, and also whenever the badges dom node is created + * That happens after either the expression renderer or the visualization error panel is rendered. + * + * You should not call this method on its own. Use renderUserMessages instead. + */ + private renderBadgeMessages = () => { const messages = this.getUserMessages('embeddableBadge'); if (messages.length && this.badgeDomNode) { @@ -923,7 +1023,7 @@ export class Embeddable this.badgeDomNode ); } - } + }; private readonly hasCompatibleActions = async ( event: ExpressionRendererEvent @@ -1186,7 +1286,10 @@ export class Embeddable ]); } - if (this.hasAnyErrors) { + const blockingErrors = this.getUserMessages(blockingMessageDisplayLocations, { + severity: 'error', + }); + if (blockingErrors.length) { this.logError('validation'); } diff --git a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx index 717e28d94ab7a..dbe950661b2fa 100644 --- a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx @@ -7,8 +7,6 @@ import React from 'react'; import { I18nProvider } from '@kbn/i18n-react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFlexGroup, EuiFlexItem, EuiText, EuiIcon, EuiEmptyPrompt } from '@elastic/eui'; import { ExpressionRendererEvent, ReactExpressionRendererProps, @@ -20,12 +18,11 @@ import { DefaultInspectorAdapters, RenderMode } from '@kbn/expressions-plugin/co import classNames from 'classnames'; import { getOriginalRequestErrorMessages } from '../editor_frame_service/error_helper'; import { LensInspector } from '../lens_inspector_service'; -import { UserMessage } from '../types'; +import { AddUserMessages } from '../types'; export interface ExpressionWrapperProps { ExpressionRenderer: ReactExpressionRendererType; expression: string | null; - errors: UserMessage[]; variables?: Record; interactive?: boolean; searchContext: ExecutionContextSearch; @@ -44,64 +41,13 @@ export interface ExpressionWrapperProps { getCompatibleCellValueActions?: ReactExpressionRendererProps['getCompatibleCellValueActions']; style?: React.CSSProperties; className?: string; - canEdit: boolean; + addUserMessages: AddUserMessages; onRuntimeError: (message?: string) => void; executionContext?: KibanaExecutionContext; lensInspector: LensInspector; noPadding?: boolean; } -interface VisualizationErrorProps { - errors: ExpressionWrapperProps['errors']; - canEdit: boolean; -} - -export function VisualizationErrorPanel({ errors, canEdit }: VisualizationErrorProps) { - const showMore = errors.length > 1; - const canFixInLens = canEdit && errors.some(({ fixableInEditor }) => fixableInEditor); - return ( -
- - {errors.length ? ( - <> -

{errors[0].longMessage}

- {showMore && !canFixInLens ? ( -

- -

- ) : null} - {canFixInLens ? ( -

- -

- ) : null} - - ) : ( -

- -

- )} - - } - /> -
- ); -} - export function ExpressionWrapper({ ExpressionRenderer: ExpressionRendererComponent, expression, @@ -120,60 +66,53 @@ export function ExpressionWrapper({ getCompatibleCellValueActions, style, className, - errors, - canEdit, onRuntimeError, + addUserMessages, executionContext, lensInspector, noPadding, }: ExpressionWrapperProps) { + if (!expression) return null; return ( - {errors.length || expression === null || expression === '' ? ( - - ) : ( -
- { - const messages = getOriginalRequestErrorMessages(error); - onRuntimeError(messages[0] ?? errorMessage); +
+ { + const messages = getOriginalRequestErrorMessages(error); + addUserMessages( + messages.map((message) => ({ + uniqueId: message, + severity: 'error', + displayLocations: [{ id: 'visualizationOnEmbeddable' }], + longMessage: message, + shortMessage: message, + fixableInEditor: false, + })) + ); + onRuntimeError(messages[0] ?? errorMessage); - return ( -
- - - - - - {messages.map((message) => ( - {message} - ))} - - -
- ); - }} - onEvent={handleEvent} - hasCompatibleActions={hasCompatibleActions} - getCompatibleCellValueActions={getCompatibleCellValueActions} - /> -
- )} + return <>; // the embeddable will take care of displaying the messages + }} + onEvent={handleEvent} + hasCompatibleActions={hasCompatibleActions} + getCompatibleCellValueActions={getCompatibleCellValueActions} + /> +
); } diff --git a/x-pack/test/functional/fixtures/kbn_archiver/lens/missing_fields.json b/x-pack/test/functional/fixtures/kbn_archiver/lens/missing_fields.json index 5668dea137400..20f8c90f92ec0 100644 --- a/x-pack/test/functional/fixtures/kbn_archiver/lens/missing_fields.json +++ b/x-pack/test/functional/fixtures/kbn_archiver/lens/missing_fields.json @@ -289,4 +289,4 @@ "type": "dashboard", "updated_at": "2023-02-07T14:59:20.822Z", "version": "WzM5MCwxXQ==" -} +} \ No newline at end of file From 10c2f4ae5af85f040c47ba2e310b570d230ee567 Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Wed, 15 Feb 2023 15:33:15 +0100 Subject: [PATCH 20/68] [Logs and Metrics UI] Make spaces optional plugin (#151053) Closes #149973 # Summary This PR makes `spaces` an optional plugin. In Logs UI we use only the space id and there we want to fallback to the default space id when the space plugin is disabled. The difference to the changes in the other PRs is that we will consider the default space as "active" space if the space plugin is disabled: so in this case we will return the default space id as this is the only property we need from the `getActiveSpace` response. # Testing 1. In the `kibana.dev.yaml` add `xpack.spaces.enabled: false` 2. Before we have this [PR](https://github.com/elastic/kibana/pull/151147) merged you should do [this change](https://github.com/elastic/kibana/pull/151147/files#diff-1b17eae66f358505fae8d86df37e155a25e8db996fce93ee6016582fb341092e) on the branch while testing 3. Inside Logs the `Anomalies` and `Categories` pages should load the default space --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/infra/kibana.jsonc | 2 +- .../plugins/infra/public/hooks/use_kibana_space.ts | 14 ++++++++++++-- x-pack/plugins/infra/public/types.ts | 2 +- .../adapters/framework/kibana_framework_adapter.ts | 13 ++----------- x-pack/plugins/infra/server/plugin.ts | 3 ++- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/infra/kibana.jsonc b/x-pack/plugins/infra/kibana.jsonc index 4e0845922ad8c..bb0e19db45e97 100644 --- a/x-pack/plugins/infra/kibana.jsonc +++ b/x-pack/plugins/infra/kibana.jsonc @@ -15,7 +15,6 @@ "share", "features", "usageCollection", - "spaces", "embeddable", "data", "dataViews", @@ -28,6 +27,7 @@ "unifiedSearch" ], "optionalPlugins": [ + "spaces", "ml", "home", "embeddable", diff --git a/x-pack/plugins/infra/public/hooks/use_kibana_space.ts b/x-pack/plugins/infra/public/hooks/use_kibana_space.ts index 5a90327ff0581..1b468c2b76c11 100644 --- a/x-pack/plugins/infra/public/hooks/use_kibana_space.ts +++ b/x-pack/plugins/infra/public/hooks/use_kibana_space.ts @@ -5,8 +5,8 @@ * 2.0. */ -import useAsync from 'react-use/lib/useAsync'; import type { Space } from '@kbn/spaces-plugin/public'; +import useAsync from 'react-use/lib/useAsync'; import { useKibanaContextForPlugin } from './use_kibana'; export type ActiveSpace = @@ -14,10 +14,20 @@ export type ActiveSpace = | { isLoading: false; error: Error; space: undefined } | { isLoading: false; error: undefined; space: Space }; +// Fallback to default if spaces plugin is not available +const getDefaultSpaceAsPromise = () => + Promise.resolve({ + id: 'default', + name: 'Default', + disabledFeatures: [], + }); + export const useActiveKibanaSpace = (): ActiveSpace => { const kibana = useKibanaContextForPlugin(); + const getActiveSpaceOrDefault = + kibana.services?.spaces?.getActiveSpace ?? getDefaultSpaceAsPromise; - const asyncActiveSpace = useAsync(kibana.services.spaces.getActiveSpace); + const asyncActiveSpace = useAsync(getActiveSpaceOrDefault); if (asyncActiveSpace.loading) { return { diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index cf4f3a0c8b934..97bf31d102558 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -71,7 +71,7 @@ export interface InfraClientStartDeps { unifiedSearch: UnifiedSearchPublicPluginStart; dataViews: DataViewsPublicPluginStart; observability: ObservabilityPublicStart; - spaces: SpacesPluginStart; + spaces?: SpacesPluginStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; usageCollection: UsageCollectionStart; ml: MlPluginStart; diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts index 5c6cde7480c7f..7080a513da82c 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -11,6 +11,7 @@ import { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/serve import { CoreSetup, IRouter, KibanaRequest, RequestHandler, RouteMethod } from '@kbn/core/server'; import { UI_SETTINGS } from '@kbn/data-plugin/server'; import { TimeseriesVisData } from '@kbn/vis-type-timeseries-plugin/server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { TSVBMetricModel } from '../../../../common/inventory_models/types'; import { InfraConfig } from '../../../plugin'; import type { InfraPluginRequestHandlerContext } from '../../../types'; @@ -213,17 +214,7 @@ export class KibanaFramework { } public getSpaceId(request: KibanaRequest): string { - const spacesPlugin = this.plugins.spaces; - - if ( - spacesPlugin && - spacesPlugin.spacesService && - typeof spacesPlugin.spacesService.getSpaceId === 'function' - ) { - return spacesPlugin.spacesService.getSpaceId(request); - } else { - return 'default'; - } + return this.plugins.spaces?.spacesService?.getSpaceId(request) ?? DEFAULT_SPACE_ID; } public async makeTSVBRequest( diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index a7fa9ceacd3c9..67563f857aedb 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -16,6 +16,7 @@ import { import { handleEsError } from '@kbn/es-ui-shared-plugin/server'; import { i18n } from '@kbn/i18n'; import { Logger } from '@kbn/logging'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants'; import { defaultLogViewsStaticConfig } from '../common/log_views'; import { publicConfigKeys } from '../common/plugin_config_types'; @@ -196,7 +197,7 @@ export class InfraServerPlugin const soClient = (await context.core).savedObjects.client; const mlSystem = plugins.ml?.mlSystemProvider(request, soClient); const mlAnomalyDetectors = plugins.ml?.anomalyDetectorsProvider(request, soClient); - const spaceId = plugins.spaces?.spacesService.getSpaceId(request) || 'default'; + const spaceId = plugins.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; return { mlAnomalyDetectors, From d579b2fa9c75b76c82572c1c1e7817efdbcc245d Mon Sep 17 00:00:00 2001 From: Tim Grein Date: Wed, 15 Feb 2023 15:36:23 +0100 Subject: [PATCH 21/68] [Enterprise Search] Fix sync flyout (#151175) --- x-pack/plugins/enterprise_search/common/types/connectors.ts | 2 +- .../components/search_index/sync_jobs/sync_job_flyout.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/common/types/connectors.ts b/x-pack/plugins/enterprise_search/common/types/connectors.ts index d400259cc56ce..5869d19ae42dc 100644 --- a/x-pack/plugins/enterprise_search/common/types/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/types/connectors.ts @@ -170,7 +170,7 @@ export interface ConnectorSyncJob { completed_at: string | null; connector: { configuration: ConnectorConfiguration; - filtering: FilteringRules[] | null; + filtering: FilteringRules | FilteringRules[] | null; id: string; index_name: string; language: string; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx index 13a89d7d4eedd..95fbefa8c773b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx @@ -33,7 +33,11 @@ interface SyncJobFlyoutProps { } export const SyncJobFlyout: React.FC = ({ onClose, syncJob }) => { - const filtering = syncJob?.connector.filtering ? syncJob.connector.filtering[0] : null; + const filtering = syncJob?.connector.filtering + ? Array.isArray(syncJob?.connector.filtering) + ? syncJob?.connector.filtering?.[0] + : syncJob?.connector.filtering + : null; const visible = !!syncJob; return visible ? ( From de87ac12c83e933eabe8334849ea1d580dfa3e91 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 15 Feb 2023 09:56:21 -0500 Subject: [PATCH 22/68] [Fleet] Deleting a FleetProxy should bump related agent policies (#151205) --- .../server/routes/fleet_proxies/handler.ts | 87 ++++++---- .../server/services/fleet_proxies.test.ts | 155 ++++++++++++++++++ .../fleet/server/services/fleet_proxies.ts | 75 ++++++++- .../apis/fleet_proxies/crud.ts | 74 +++++++++ 4 files changed, 353 insertions(+), 38 deletions(-) create mode 100644 x-pack/plugins/fleet/server/services/fleet_proxies.test.ts diff --git a/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts b/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts index ed77dddb4bf88..5b00baf9bf2c1 100644 --- a/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts +++ b/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts @@ -6,7 +6,11 @@ */ import type { RequestHandler } from '@kbn/core/server'; -import { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import { + SavedObjectsErrorHelpers, + type SavedObjectsClientContract, + type ElasticsearchClient, +} from '@kbn/core/server'; import type { TypeOf } from '@kbn/config-schema'; import pMap from 'p-map'; @@ -16,15 +20,51 @@ import { deleteFleetProxy, getFleetProxy, updateFleetProxy, + getFleetProxyRelatedSavedObjects, } from '../../services/fleet_proxies'; import { defaultFleetErrorHandler } from '../../errors'; import type { GetOneFleetProxyRequestSchema, PostFleetProxyRequestSchema, PutFleetProxyRequestSchema, + FleetServerHost, + Output, } from '../../types'; -import { listFleetServerHostsForProxyId } from '../../services/fleet_server_host'; -import { agentPolicyService, outputService } from '../../services'; +import { agentPolicyService } from '../../services'; + +async function bumpRelatedPolicies( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + fleetServerHosts: FleetServerHost[], + outputs: Output[] +) { + if ( + fleetServerHosts.some((host) => host.is_default) || + outputs.some((output) => output.is_default || output.is_default_monitoring) + ) { + await agentPolicyService.bumpAllAgentPolicies(soClient, esClient); + } else { + await pMap( + outputs, + (output) => agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, output.id), + { + concurrency: 20, + } + ); + await pMap( + fleetServerHosts, + (fleetServerHost) => + agentPolicyService.bumpAllAgentPoliciesForFleetServerHosts( + soClient, + esClient, + fleetServerHost.id + ), + { + concurrency: 20, + } + ); + } +} export const postFleetProxyHandler: RequestHandler< undefined, @@ -64,36 +104,8 @@ export const putFleetProxyHandler: RequestHandler< }; // Bump all the agent policy that use that proxy - const [{ items: fleetServerHosts }, { items: outputs }] = await Promise.all([ - listFleetServerHostsForProxyId(soClient, proxyId), - outputService.listAllForProxyId(soClient, proxyId), - ]); - if ( - fleetServerHosts.some((host) => host.is_default) || - outputs.some((output) => output.is_default || output.is_default_monitoring) - ) { - await agentPolicyService.bumpAllAgentPolicies(soClient, esClient); - } else { - await pMap( - outputs, - (output) => agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, output.id), - { - concurrency: 20, - } - ); - await pMap( - fleetServerHosts, - (fleetServerHost) => - agentPolicyService.bumpAllAgentPoliciesForFleetServerHosts( - soClient, - esClient, - fleetServerHost.id - ), - { - concurrency: 20, - } - ); - } + const { fleetServerHosts, outputs } = await getFleetProxyRelatedSavedObjects(soClient, proxyId); + await bumpRelatedPolicies(soClient, esClient, fleetServerHosts, outputs); return response.ok({ body }); } catch (error) { @@ -109,6 +121,7 @@ export const putFleetProxyHandler: RequestHandler< export const getAllFleetProxyHandler: RequestHandler = async (context, request, response) => { const soClient = (await context.core).savedObjects.client; + try { const res = await listFleetProxies(soClient); const body = { @@ -128,9 +141,17 @@ export const deleteFleetProxyHandler: RequestHandler< TypeOf > = async (context, request, response) => { try { + const proxyId = request.params.itemId; const coreContext = await context.core; const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; + + const { fleetServerHosts, outputs } = await getFleetProxyRelatedSavedObjects(soClient, proxyId); + await deleteFleetProxy(soClient, request.params.itemId); + + await bumpRelatedPolicies(soClient, esClient, fleetServerHosts, outputs); + const body = { id: request.params.itemId, }; diff --git a/x-pack/plugins/fleet/server/services/fleet_proxies.test.ts b/x-pack/plugins/fleet/server/services/fleet_proxies.test.ts new file mode 100644 index 0000000000000..024860e0b6490 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/fleet_proxies.test.ts @@ -0,0 +1,155 @@ +/* + * 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 { savedObjectsClientMock } from '@kbn/core/server/mocks'; + +import { FLEET_PROXY_SAVED_OBJECT_TYPE } from '../constants'; + +import { deleteFleetProxy } from './fleet_proxies'; +import { listFleetServerHostsForProxyId, updateFleetServerHost } from './fleet_server_host'; +import { outputService } from './output'; + +jest.mock('./output'); +jest.mock('./fleet_server_host'); + +const mockedListFleetServerHostsForProxyId = listFleetServerHostsForProxyId as jest.MockedFunction< + typeof listFleetServerHostsForProxyId +>; + +const mockedUpdateFleetServerHost = updateFleetServerHost as jest.MockedFunction< + typeof updateFleetServerHost +>; + +const mockedOutputService = outputService as jest.Mocked; + +const PROXY_IDS = { + PRECONFIGURED: 'test-preconfigured', + RELATED_PRECONFIGURED: 'test-related-preconfigured', +}; + +describe('Fleet proxies service', () => { + const soClientMock = savedObjectsClientMock.create(); + + beforeEach(() => { + mockedOutputService.update.mockReset(); + soClientMock.delete.mockReset(); + mockedUpdateFleetServerHost.mockReset(); + mockedOutputService.listAllForProxyId.mockImplementation(async (_, proxyId) => { + if (proxyId === PROXY_IDS.RELATED_PRECONFIGURED) { + return { + items: [ + { + id: 'test', + is_preconfigured: true, + type: 'elasticsearch', + name: 'test', + proxy_id: proxyId, + is_default: false, + is_default_monitoring: false, + }, + ], + total: 1, + page: 1, + perPage: 10, + }; + } + + return { + items: [], + total: 0, + page: 1, + perPage: 10, + }; + }); + mockedListFleetServerHostsForProxyId.mockImplementation(async (_, proxyId) => { + if (proxyId === PROXY_IDS.RELATED_PRECONFIGURED) { + return { + items: [ + { + id: 'test', + is_preconfigured: true, + host_urls: ['http://test.fr'], + is_default: false, + name: 'test', + proxy_id: proxyId, + }, + ], + total: 1, + page: 1, + perPage: 10, + }; + } + + return { + items: [], + total: 0, + page: 1, + perPage: 10, + }; + }); + soClientMock.get.mockImplementation(async (type, id) => { + if (type !== FLEET_PROXY_SAVED_OBJECT_TYPE) { + throw new Error(`${type} not mocked in SO client`); + } + + if (id === PROXY_IDS.PRECONFIGURED) { + return { + id, + type, + attributes: { + is_preconfigured: true, + }, + references: [], + }; + } + + if (id === PROXY_IDS.RELATED_PRECONFIGURED) { + return { + id, + type, + attributes: { + is_preconfigured: false, + }, + references: [], + }; + } + + throw new Error(`${id} not found`); + }); + }); + + describe('delete', () => { + it('should not allow to delete preconfigured proxy', async () => { + await expect(() => + deleteFleetProxy(soClientMock, PROXY_IDS.PRECONFIGURED) + ).rejects.toThrowError(/Cannot delete test-preconfigured preconfigured proxy/); + }); + + it('should allow to delete preconfigured proxy with option fromPreconfiguration:true', async () => { + await deleteFleetProxy(soClientMock, PROXY_IDS.PRECONFIGURED, { fromPreconfiguration: true }); + + expect(soClientMock.delete).toBeCalled(); + }); + + it('should not allow to delete proxy wiht related preconfigured saved object', async () => { + await expect(() => + deleteFleetProxy(soClientMock, PROXY_IDS.RELATED_PRECONFIGURED) + ).rejects.toThrowError( + /Cannot delete a proxy used in a preconfigured fleet server hosts or output./ + ); + }); + + it('should allow to delete proxy wiht related preconfigured saved object option fromPreconfiguration:true', async () => { + await deleteFleetProxy(soClientMock, PROXY_IDS.RELATED_PRECONFIGURED, { + fromPreconfiguration: true, + }); + expect(mockedOutputService.update).toBeCalled(); + expect(mockedUpdateFleetServerHost).toBeCalled(); + expect(soClientMock.delete).toBeCalled(); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/fleet_proxies.ts b/x-pack/plugins/fleet/server/services/fleet_proxies.ts index 17ab88a889034..09868568e84a8 100644 --- a/x-pack/plugins/fleet/server/services/fleet_proxies.ts +++ b/x-pack/plugins/fleet/server/services/fleet_proxies.ts @@ -6,10 +6,21 @@ */ import type { SavedObjectsClientContract, SavedObject } from '@kbn/core/server'; +import { omit } from 'lodash'; +import pMap from 'p-map'; import { FLEET_PROXY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../constants'; import { FleetProxyUnauthorizedError } from '../errors'; -import type { FleetProxy, FleetProxySOAttributes, NewFleetProxy } from '../types'; +import type { + FleetProxy, + FleetProxySOAttributes, + FleetServerHost, + NewFleetProxy, + Output, +} from '../types'; + +import { listFleetServerHostsForProxyId, updateFleetServerHost } from './fleet_server_host'; +import { outputService } from './output'; function savedObjectToFleetProxy(so: SavedObject): FleetProxy { const { proxy_headers: proxyHeaders, ...rest } = so.attributes; @@ -79,14 +90,25 @@ export async function deleteFleetProxy( id: string, options?: { fromPreconfiguration?: boolean } ) { - const fleetServerHost = await getFleetProxy(soClient, id); + const fleetProxy = await getFleetProxy(soClient, id); - if (fleetServerHost.is_preconfigured && !options?.fromPreconfiguration) { + if (fleetProxy.is_preconfigured && !options?.fromPreconfiguration) { throw new FleetProxyUnauthorizedError(`Cannot delete ${id} preconfigured proxy`); } + const { outputs, fleetServerHosts } = await getFleetProxyRelatedSavedObjects(soClient, id); + + if ( + [...fleetServerHosts, ...outputs].some( + (fleetServerHostOrOutput) => fleetServerHostOrOutput.is_preconfigured + ) && + !options?.fromPreconfiguration + ) { + throw new FleetProxyUnauthorizedError( + 'Cannot delete a proxy used in a preconfigured fleet server hosts or output.' + ); + } - // TODO remove from all outputs and fleet server - // await agentPolicyService.removeFleetServerHostFromAll(soClient, esClient, id); + await updateRelatedSavedObject(soClient, fleetServerHosts, outputs); return await soClient.delete(FLEET_PROXY_SAVED_OBJECT_TYPE, id); } @@ -147,3 +169,46 @@ export async function bulkGetFleetProxies( typeof fleetProxyOrUndefined !== 'undefined' ); } + +async function updateRelatedSavedObject( + soClient: SavedObjectsClientContract, + fleetServerHosts: FleetServerHost[], + outputs: Output[] +) { + await pMap( + fleetServerHosts, + (fleetServerHost) => { + updateFleetServerHost(soClient, fleetServerHost.id, { + ...omit(fleetServerHost, 'id'), + proxy_id: null, + }); + }, + { concurrency: 20 } + ); + + await pMap( + outputs, + (output) => { + outputService.update(soClient, output.id, { + ...omit(output, 'id'), + proxy_id: null, + }); + }, + { concurrency: 20 } + ); +} + +export async function getFleetProxyRelatedSavedObjects( + soClient: SavedObjectsClientContract, + proxyId: string +) { + const [{ items: fleetServerHosts }, { items: outputs }] = await Promise.all([ + listFleetServerHostsForProxyId(soClient, proxyId), + outputService.listAllForProxyId(soClient, proxyId), + ]); + + return { + fleetServerHosts, + outputs, + }; +} diff --git a/x-pack/test/fleet_api_integration/apis/fleet_proxies/crud.ts b/x-pack/test/fleet_api_integration/apis/fleet_proxies/crud.ts index 91895867404be..b769b9fdb2618 100644 --- a/x-pack/test/fleet_api_integration/apis/fleet_proxies/crud.ts +++ b/x-pack/test/fleet_api_integration/apis/fleet_proxies/crud.ts @@ -15,6 +15,21 @@ export default function (providerContext: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); + const es = getService('es'); + + async function getLatestFleetPolicies(policyId: string): Promise { + const policyDocRes = await es.search({ + index: '.fleet-policies', + query: { + term: { + policy_id: policyId, + }, + }, + sort: [{ '@timestamp': 'desc' }], + }); + + return policyDocRes.hits.hits[0]?._source; + } describe('fleet_proxies_crud', async function () { skipIfNoDockerRegistry(providerContext); @@ -25,6 +40,9 @@ export default function (providerContext: FtrProviderContext) { setupFleetAndAgents(providerContext); const existingId = 'test-default-123'; + const fleetServerHostId = 'test-fleetserver-123'; + const policyId = 'test-policy-123'; + const outputId = 'test-output-123'; before(async function () { await kibanaServer.savedObjects.clean({ @@ -39,10 +57,46 @@ export default function (providerContext: FtrProviderContext) { url: 'https://test.fr:3232', }) .expect(200); + await supertest + .post(`/api/fleet/fleet_server_hosts`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: fleetServerHostId, + name: 'Test 123', + host_urls: ['https://fleetserverhost.fr:3232'], + proxy_id: existingId, + }) + .expect(200); + await supertest + .post(`/api/fleet/outputs`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: outputId, + name: 'Test 123', + type: 'elasticsearch', + hosts: ['http://es:9200'], + proxy_id: existingId, + }) + .expect(200); + + await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + id: policyId, + name: 'Test 123', + namespace: 'default', + fleet_server_host_id: fleetServerHostId, + data_output_id: outputId, + }) + .expect(200); }); after(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.savedObjects.clean({ + types: ['fleet-proxy'], + }); await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); }); @@ -82,6 +136,7 @@ export default function (providerContext: FtrProviderContext) { .set('kbn-xsrf', 'xxxx') .send({ name: 'Test 123 updated', + url: 'https://testupdated.fr:3232', }) .expect(200); @@ -90,6 +145,12 @@ export default function (providerContext: FtrProviderContext) { } = await supertest.get(`/api/fleet/proxies/${existingId}`).expect(200); expect(fleetServerHost.name).to.eql('Test 123 updated'); + + const fleetPolicyAfter = await getLatestFleetPolicies(policyId); + expect(fleetPolicyAfter?.data?.fleet?.proxy_url).to.be('https://testupdated.fr:3232'); + expect(fleetPolicyAfter?.data?.outputs?.[outputId].proxy_url).to.be( + 'https://testupdated.fr:3232' + ); }); it('should return a 404 when updating a non existing fleet proxy', async function () { @@ -102,5 +163,18 @@ export default function (providerContext: FtrProviderContext) { .expect(404); }); }); + + describe('DELETE /proxies/{itemId}', () => { + it('should allow to delete an existing fleet proxy', async function () { + await supertest + .delete(`/api/fleet/proxies/${existingId}`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + + const fleetPolicyAfter = await getLatestFleetPolicies(policyId); + expect(fleetPolicyAfter?.data?.fleet?.proxy_url).to.be(undefined); + expect(fleetPolicyAfter?.data?.outputs?.[outputId].proxy_url).to.be(undefined); + }); + }); }); } From d37a4d6fbed4b490c9d5753679dcc8eb3b57f89e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 15 Feb 2023 08:02:38 -0700 Subject: [PATCH 23/68] [Maps] fix Map crashes on field edit when hitting backspace key (#151203) Fixes https://github.com/elastic/kibana/issues/151121 There were 2 issues 1) user could delete right field, putting layer in non-working state. To fix this, update handler is not called when selected value is undefined 2) createJoinTermSource guard was not working for `{ term: undefined }`. PR updated guard to properly handle this case. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../public/classes/joins/inner_join.test.js | 60 +++++++++++++++++-- .../maps/public/classes/joins/inner_join.ts | 6 +- .../sources/es_term_source/es_term_source.ts | 5 +- .../join_editor/resources/join_expression.tsx | 10 +++- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.test.js b/x-pack/plugins/maps/public/classes/joins/inner_join.test.js index f8ca092e9a164..b01d977972a68 100644 --- a/x-pack/plugins/maps/public/classes/joins/inner_join.test.js +++ b/x-pack/plugins/maps/public/classes/joins/inner_join.test.js @@ -5,7 +5,7 @@ * 2.0. */ -import { InnerJoin } from './inner_join'; +import { createJoinTermSource, InnerJoin } from './inner_join'; import { SOURCE_TYPES } from '../../../common/constants'; jest.mock('../../kibana_services', () => {}); @@ -38,8 +38,56 @@ const leftJoin = new InnerJoin( ); const COUNT_PROPERTY_NAME = '__kbnjoin__count__d3625663-5b34-4d50-a784-0d743f676a0c'; +describe('createJoinTermSource', () => { + test('Should return undefined when descriptor is not provided', () => { + expect(createJoinTermSource(undefined)).toBe(undefined); + }); + + test('Should return undefined with unmatched source type', () => { + expect( + createJoinTermSource({ + type: SOURCE_TYPES.WMS, + }) + ).toBe(undefined); + }); + + describe('EsTermSource', () => { + test('Should return EsTermSource', () => { + expect(createJoinTermSource(rightSource).constructor.name).toBe('ESTermSource'); + }); + + test('Should return undefined when indexPatternId is undefined', () => { + expect( + createJoinTermSource({ + ...rightSource, + indexPatternId: undefined, + }) + ).toBe(undefined); + }); + + test('Should return undefined when term is undefined', () => { + expect( + createJoinTermSource({ + ...rightSource, + term: undefined, + }) + ).toBe(undefined); + }); + }); + + describe('TableSource', () => { + test('Should return TableSource', () => { + expect( + createJoinTermSource({ + type: SOURCE_TYPES.TABLE_SOURCE, + }).constructor.name + ).toBe('TableSource'); + }); + }); +}); + describe('joinPropertiesToFeature', () => { - it('Should add join property to features in feature collection', () => { + test('Should add join property to features in feature collection', () => { const feature = { properties: { iso2: 'CN', @@ -59,7 +107,7 @@ describe('joinPropertiesToFeature', () => { }); }); - it('Should delete previous join property values from feature', () => { + test('Should delete previous join property values from feature', () => { const feature = { properties: { iso2: 'CN', @@ -79,7 +127,7 @@ describe('joinPropertiesToFeature', () => { }); }); - it('Should coerce to string before joining', () => { + test('Should coerce to string before joining', () => { const leftJoin = new InnerJoin( { leftField: 'zipcode', @@ -107,7 +155,7 @@ describe('joinPropertiesToFeature', () => { }); }); - it('Should handle undefined values', () => { + test('Should handle undefined values', () => { const feature = { //this feature does not have the iso2 field properties: { @@ -127,7 +175,7 @@ describe('joinPropertiesToFeature', () => { }); }); - it('Should handle falsy values', () => { + test('Should handle falsy values', () => { const leftJoin = new InnerJoin( { leftField: 'code', diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.ts b/x-pack/plugins/maps/public/classes/joins/inner_join.ts index b8b96352625fc..b3a55ea55decd 100644 --- a/x-pack/plugins/maps/public/classes/joins/inner_join.ts +++ b/x-pack/plugins/maps/public/classes/joins/inner_join.ts @@ -26,7 +26,7 @@ import { PropertiesMap } from '../../../common/elasticsearch_util'; import { ITermJoinSource } from '../sources/term_join_source'; import { TableSource } from '../sources/table_source'; -function createJoinTermSource( +export function createJoinTermSource( descriptor: Partial | undefined ): ITermJoinSource | undefined { if (!descriptor) { @@ -35,8 +35,8 @@ function createJoinTermSource( if ( descriptor.type === SOURCE_TYPES.ES_TERM_SOURCE && - 'indexPatternId' in descriptor && - 'term' in descriptor + descriptor.indexPatternId !== undefined && + descriptor.term !== undefined ) { return new ESTermSource(descriptor as ESTermSourceDescriptor); } else if (descriptor.type === SOURCE_TYPES.TABLE_SOURCE) { diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts index 71ce42be22ab0..a8a756eeb3aed 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts @@ -5,7 +5,6 @@ * 2.0. */ -import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import type { Query } from '@kbn/es-query'; import { ISearchSource } from '@kbn/data-plugin/public'; @@ -42,7 +41,7 @@ type ESTermSourceSyncMeta = Pick(); - const buckets: any[] = _.get(rawEsData, ['aggregations', TERMS_AGG_NAME, 'buckets'], []); + const buckets: any[] = rawEsData?.aggregations?.[TERMS_AGG_NAME]?.buckets ?? []; buckets.forEach((termBucket: any) => { const properties = extractPropertiesFromBucket(termBucket, TERMS_BUCKET_KEYS_TO_IGNORE); if (countPropertyName) { @@ -83,7 +82,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource } hasCompleteConfig(): boolean { - return _.has(this._descriptor, 'indexPatternId') && _.has(this._descriptor, 'term'); + return this._descriptor.indexPatternId !== undefined && this._descriptor.term !== undefined; } getTermField(): ESDocField { diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join_expression.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join_expression.tsx index 9e55833698c7d..c70730c0f3f62 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join_expression.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join_expression.tsx @@ -84,6 +84,14 @@ export class JoinExpression extends Component { this.props.onLeftFieldChange(_.get(selectedFields, '[0].value.name', null)); }; + _onRightFieldChange = (term?: string) => { + if (!term || term.length === 0) { + return; + } + + this.props.onRightFieldChange(term); + }; + _renderLeftFieldSelect() { const { leftValue, leftFields } = this.props; @@ -167,7 +175,7 @@ export class JoinExpression extends Component { From 56d64544bbb683cd380c18c6bbdf64f199c41567 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Wed, 15 Feb 2023 16:05:15 +0100 Subject: [PATCH 24/68] [SecuritySolutions] Fix show-top-n not rendered for some security fields (#151257) I went through every usage of `CellActions` and added the `aggregatable` prop back to fields that previously had it. ### Why? When I created CellActions I removed `isAgreggatable` from some fields because every field with an aggregatable type would automatically display `showTopN`. But later, I discovered that this approach didn't work for some edge cases. I reintroduced the `isAgreggatable` prop but forgot to add `isAgreggatable` back to those fields I had previously removed it. ### Extra It also fixes the network page country flag type from`geo_point` to `keyword` to fix a bug. It also adds an extra check to add_to_timeline `isCompatible` function so it doesn't show for `geo_point` and `message` fields. ![Screenshot 2023-02-15 at 11 53 35](https://user-images.githubusercontent.com/1490444/219011030-c3be4cc4-c095-4b89-8a64-5e29d0b3ba1b.png) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../actions/add_to_timeline/cell_action/add_to_timeline.ts | 5 +++-- .../public/actions/add_to_timeline/data_provider.ts | 5 ++++- .../hosts/components/host_risk_score_table/columns.tsx | 1 + .../public/explore/hosts/components/hosts_table/columns.tsx | 1 + .../explore/network/components/network_dns_table/columns.tsx | 1 + .../components/network_top_countries_table/columns.tsx | 1 + .../network/components/network_top_n_flow_table/columns.tsx | 4 +++- .../users/components/user_risk_score_table/columns.tsx | 1 + 8 files changed, 15 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts index c444a46cd6407..1fb2f5cee22f9 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts @@ -18,7 +18,7 @@ import { ADD_TO_TIMELINE_ICON, ADD_TO_TIMELINE_SUCCESS_TITLE, } from '../constants'; -import { createDataProviders } from '../data_provider'; +import { createDataProviders, isValidDataProviderField } from '../data_provider'; import { SecurityCellActionType } from '../../constants'; import type { StartServices } from '../../../types'; import type { SecurityCellAction } from '../../types'; @@ -38,7 +38,8 @@ export const createAddToTimelineCellActionFactory = createCellActionFactory( getIconType: () => ADD_TO_TIMELINE_ICON, getDisplayName: () => ADD_TO_TIMELINE, getDisplayNameTooltip: () => ADD_TO_TIMELINE, - isCompatible: async ({ field }) => fieldHasCellActions(field.name), + isCompatible: async ({ field }) => + fieldHasCellActions(field.name) && isValidDataProviderField(field.name, field.type), execute: async ({ field, metadata }) => { const dataProviders = createDataProviders({ diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts index a7593a93849de..e7c6cfcbe46be 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts @@ -85,7 +85,7 @@ export const createDataProviders = ({ value ? `-${value}` : '' }`; - if (fieldType === GEO_FIELD_TYPE || field === MESSAGE_FIELD_NAME) { + if (!isValidDataProviderField(field, fieldType)) { return dataProviders; } @@ -131,6 +131,9 @@ export const createDataProviders = ({ }, []); }; +export const isValidDataProviderField = (fieldName: string, fieldType: string | undefined) => + fieldType !== GEO_FIELD_TYPE && fieldName !== MESSAGE_FIELD_NAME; + const getIdForField = ({ field, fieldFormat, diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx index 3d7729a5e9ac7..557dcff247a6b 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx @@ -44,6 +44,7 @@ export const getHostRiskScoreColumns = ({ name: 'host.name', value: hostName, type: 'keyword', + aggregatable: true, }} > diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx index 1dcff6fbb0afd..9078a103b389d 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx @@ -44,6 +44,7 @@ export const getHostsColumns = ( name: 'host.name', value: hostName[0], type: 'keyword', + aggregatable: true, }} > diff --git a/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx index c343742143ac2..ee87bab1208fd 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/components/network_dns_table/columns.tsx @@ -50,6 +50,7 @@ export const getNetworkDnsColumns = (): NetworkDnsColumns => [ name: 'dns.question.registered_domain', value: dnsName, type: 'keyword', + aggregatable: true, }} > {defaultToEmptyTag(dnsName)} diff --git a/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx index d7b443e1ab326..314bd13042dc6 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/components/network_top_countries_table/columns.tsx @@ -68,6 +68,7 @@ export const getNetworkTopCountriesColumns = ( name: geoAttr, value: geo, type: 'keyword', + aggregatable: true, }} > diff --git a/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx index 6d11df641286b..9ece66b14df97 100644 --- a/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/components/network_top_n_flow_table/columns.tsx @@ -76,6 +76,7 @@ export const getNetworkTopNFlowColumns = ( name: ipAttr, value: ip, type: 'keyword', + aggregatable: true, }} > @@ -91,7 +92,8 @@ export const getNetworkTopNFlowColumns = ( field={{ name: geoAttrName, value: geo, - type: 'geo_point', + type: 'keyword', + aggregatable: true, }} > {' '} diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx index 3fd6d86cc0b92..86a57109ac431 100644 --- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx @@ -47,6 +47,7 @@ export const getUserRiskScoreColumns = ({ name: 'user.name', value: userName, type: 'keyword', + aggregatable: true, }} > From 8244a9f71362a49af0c3fe2a9da0b7f206c5b5fc Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 15 Feb 2023 16:20:46 +0100 Subject: [PATCH 25/68] Remove unused minimist resolution (#151291) --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 66dd8cb08e49f..996b65f226ccb 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "**/isomorphic-fetch/node-fetch": "^2.6.7", "**/istanbul-lib-coverage": "^3.2.0", "**/minimatch": "^3.1.2", - "**/minimist": "^1.2.6", "**/pdfkit/crypto-js": "4.0.0", "**/react-syntax-highlighter": "^15.3.1", "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", From 5de13d49acb69977d92498cc08e5bf03a7b365ff Mon Sep 17 00:00:00 2001 From: Jeramy Soucy Date: Wed, 15 Feb 2023 10:25:05 -0500 Subject: [PATCH 26/68] [Saved Objects] Migrates authorization logic from repository to security extension (#148165) Closes #147049 Closes #149897 Migrates authorization and audit logic from the Saved Objects Repository to the Saved Objects Security Extension. This is achieved by implementing action-specific authorization methods within the security extension. The SO repository is no longer responsible for making any authorization decisions, but It is still responsible to know how to call the extension methods. I've tried to make this as straightforward as possible such that there is a clear ownership delineation between the repository and the extension, by keeping the interface simple and (hopefully) obvious. ### Security Extension Interface New Public Extension Methods: - authorizeCreate - authorizeBulkCreate - authorizeUpdate - authorizeBulkUpdate - authorizeDelete - authorizeBulkDelete - authorizeGet - authorizeBulkGet - authorizeCheckConflicts - authorizeRemoveReferences - authorizeOpenPointInTime - auditClosePointInTime - authorizeAndRedactMultiNamespaceReferences - authorizeAndRedactInternalBulkResolve - authorizeUpdateSpaces - authorizeFind - getFindRedactTypeMap - authorizeDisableLegacyUrlAliases (for secure spaces client) - auditObjectsForSpaceDeletion (for secure spaces client) Removed from public interface: - authorize - enforceAuthorization - addAuditEvent ### Tests - Most test coverage moved from `repository.security_extension.test.ts` to `saved_objects_security_extension.test.ts` - `repository.security_extension.test.ts` tests extension call, parameters, and return - Updates repository unit tests to check that all security extension calls are made with the current space when the spaces extension is also enabled --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/user/security/audit-logging.asciidoc | 14 +- .../src/saved_objects_client.ts | 1 - .../tsconfig.json | 2 +- ...collect_multi_namespace_references.test.ts | 190 +- .../lib/collect_multi_namespace_references.ts | 211 +- .../src/lib/decorate_es_error.test.ts | 2 +- .../src/lib/decorate_es_error.ts | 2 +- .../src/lib/filter_utils.ts | 2 +- .../src/lib/internal_bulk_resolve.test.ts | 140 +- .../src/lib/internal_bulk_resolve.ts | 119 +- .../src/lib/internal_utils.ts | 15 +- .../src/lib/preflight_check_for_create.ts | 12 +- .../lib/repository.security_extension.test.ts | 1477 ++-- .../lib/repository.spaces_extension.test.ts | 313 +- .../src/lib/repository.test.ts | 13 +- .../src/lib/repository.ts | 450 +- .../src/lib/repository_es_client.test.ts | 2 +- .../src/lib/update_objects_spaces.test.ts | 401 +- .../src/lib/update_objects_spaces.ts | 76 +- .../mocks/saved_objects_extensions.mock.ts | 22 +- .../src/saved_objects_client.ts | 3 +- .../test_helpers/repository.test.common.ts | 92 +- .../src/saved_objects_client.mock.ts | 2 +- .../src/saved_objects_extensions.mock.ts | 22 +- .../tsconfig.json | 1 - .../src/version/decode_version.ts | 2 +- .../src/saved_objects_client.ts | 1 - .../core-saved-objects-common/index.ts | 2 +- .../src/server_types.ts | 5 + .../core-saved-objects-common/src/types.ts | 19 + .../export/collect_exported_objects.test.ts | 2 +- .../src/export/collect_exported_objects.ts | 2 +- .../src/export/saved_objects_exporter.ts | 2 +- .../src/import/import_saved_objects.test.ts | 2 +- .../src/import/lib/check_conflicts.test.ts | 7 +- .../src/import/lib/collect_saved_objects.ts | 1 - .../import/lib/create_saved_objects.test.ts | 3 +- .../src/import/lib/create_saved_objects.ts | 3 +- .../import/lib/execute_import_hooks.test.ts | 3 +- .../src/import/lib/execute_import_hooks.ts | 3 +- .../src/import/lib/extract_errors.test.ts | 7 +- .../src/import/lib/extract_errors.ts | 3 +- .../get_import_state_map_for_retries.test.ts | 1 + .../lib/get_import_state_map_for_retries.ts | 1 + .../import/lib/validate_references.test.ts | 2 +- .../src/import/resolve_import_errors.test.ts | 3 +- .../src/import/resolve_import_errors.ts | 2 +- .../tsconfig.json | 1 - .../src/routes/utils.ts | 4 +- .../tsconfig.json | 1 - .../core-saved-objects-server/index.ts | 30 +- .../src/extensions/extensions.ts | 3 + .../src/extensions/security.ts | 526 +- .../src/saved_objects_error_helpers.test.ts | 0 .../src/saved_objects_error_helpers.ts | 448 ++ .../src/saved_objects_management.ts | 2 +- .../core-saved-objects-server/tsconfig.json | 1 - .../core-saved-objects-utils-server/index.ts | 1 - .../src/saved_objects_error_helpers.ts | 217 - .../src/clients/ui_settings_client_common.ts | 2 +- .../create_or_upgrade_saved_config.test.ts | 2 +- .../create_or_upgrade_saved_config.ts | 2 +- .../src/routes/delete.ts | 2 +- .../src/routes/get.ts | 2 +- .../src/routes/set.ts | 2 +- .../src/routes/set_many.ts | 2 +- .../src/saved_objects/transforms.test.ts | 2 +- .../src/saved_objects/transforms.ts | 2 +- .../tsconfig.json | 1 - src/core/server/index.ts | 6 +- .../saved_objects/routes/import.test.ts | 2 +- .../get_telemetry_saved_object.ts | 2 +- src/plugins/telemetry/tsconfig.json | 1 - .../services/epm/archive/storage.test.ts | 2 +- x-pack/plugins/fleet/tsconfig.json | 1 - .../server/services/slo/slo_repository.ts | 2 +- x-pack/plugins/observability/tsconfig.json | 1 - .../server/audit/audit_events.test.ts | 2 +- .../security/server/audit/audit_events.ts | 8 +- .../saved_objects_security_extension.test.ts | 6213 ++++++++++++++++- .../saved_objects_security_extension.ts | 1190 +++- .../secure_spaces_client_wrapper.test.ts | 92 +- .../spaces/secure_spaces_client_wrapper.ts | 62 +- x-pack/plugins/security/tsconfig.json | 4 + .../server/__mocks__/core.mock.ts | 3 +- x-pack/plugins/spaces/common/index.ts | 8 +- x-pack/plugins/spaces/common/types.ts | 18 - .../components/types.ts | 2 +- .../public/spaces_manager/spaces_manager.ts | 8 +- x-pack/plugins/spaces/server/index.ts | 8 +- .../server/spaces_client/spaces_client.ts | 9 +- x-pack/plugins/spaces/tsconfig.json | 1 + 92 files changed, 9507 insertions(+), 3051 deletions(-) rename packages/core/saved-objects/{core-saved-objects-utils-server => core-saved-objects-server}/src/saved_objects_error_helpers.test.ts (100%) create mode 100644 packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts delete mode 100644 packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index e4fd9111b216a..61ecb7aee11bb 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -123,18 +123,18 @@ Refer to the corresponding {es} logs for potential write errors. | `unknown` | User is updating a saved object. | `failure` | User is not authorized to update a saved object. -.2+| `saved_object_add_to_spaces` -| `unknown` | User is adding a saved object to other spaces. -| `failure` | User is not authorized to add a saved object to other spaces. - -.2+| `saved_object_delete_from_spaces` -| `unknown` | User is removing a saved object from other spaces. -| `failure` | User is not authorized to remove a saved object from other spaces. +.2+| `saved_object_update_objects_spaces` +| `unknown` | User is adding and/or removing a saved object to/from other spaces. +| `failure` | User is not authorized to add or remove a saved object to or from other spaces. .2+| `saved_object_remove_references` | `unknown` | User is removing references to a saved object. | `failure` | User is not authorized to remove references to a saved object. +.2+| `saved_object_collect_multinamespace_references` +| `success` | User has accessed references to a multi-space saved object. +| `failure` | User is not authorized to access references to a multi-space saved object. + .2+| `connector_update` | `unknown` | User is updating a connector. | `failure` | User is not authorized to update a connector. diff --git a/packages/core/saved-objects/core-saved-objects-api-browser/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-api-browser/src/saved_objects_client.ts index d92b39f6c15a6..9a3d5f1e23317 100644 --- a/packages/core/saved-objects/core-saved-objects-api-browser/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-api-browser/src/saved_objects_client.ts @@ -22,7 +22,6 @@ import type { SavedObjectsBulkDeleteResponse, SavedObjectsBulkDeleteOptions, } from './apis'; - import type { SimpleSavedObject } from './simple_saved_object'; /** diff --git a/packages/core/saved-objects/core-saved-objects-api-browser/tsconfig.json b/packages/core/saved-objects/core-saved-objects-api-browser/tsconfig.json index 271f341183fce..5d64239e4a8f3 100644 --- a/packages/core/saved-objects/core-saved-objects-api-browser/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-api-browser/tsconfig.json @@ -12,7 +12,7 @@ ], "kbn_references": [ "@kbn/core-saved-objects-common", - "@kbn/core-saved-objects-api-server" + "@kbn/core-saved-objects-api-server", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.test.ts index c9529a6828d7a..b4e788dd2973a 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.test.ts @@ -17,11 +17,6 @@ import type { SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsCollectMultiNamespaceReferencesOptions, } from '@kbn/core-saved-objects-api-server'; -import { - setMapsAreEqual, - SavedObjectsErrorHelpers, - setsAreEqual, -} from '@kbn/core-saved-objects-utils-server'; import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { @@ -30,16 +25,16 @@ import { } from './collect_multi_namespace_references'; import { collectMultiNamespaceReferences } from './collect_multi_namespace_references'; import type { CreatePointInTimeFinderFn } from './point_in_time_finder'; -import { AuditAction, type ISavedObjectsSecurityExtension } from '@kbn/core-saved-objects-server'; - import { - authMap, enforceError, - setupPerformAuthFullyAuthorized, - setupPerformAuthEnforceFailure, - setupRedactPassthrough, + setupAuthorizeAndRedactMultiNamespaceReferenencesFailure, + setupAuthorizeAndRedactMultiNamespaceReferenencesSuccess, } from '../test_helpers/repository.test.common'; import { savedObjectsExtensionsMock } from '../mocks/saved_objects_extensions.mock'; +import { + type ISavedObjectsSecurityExtension, + SavedObjectsErrorHelpers, +} from '@kbn/core-saved-objects-server'; const SPACES = ['default', 'another-space']; const VERSION_PROPS = { _seq_no: 1, _primary_term: 1 }; @@ -474,8 +469,38 @@ describe('collectMultiNamespaceReferences', () => { const obj1LegacySpaces = ['space-1', 'space-2', 'space-3', 'space-4']; let params: CollectMultiNamespaceReferencesParams; + const expectedObjects = [ + { + id: 'id-1', + inboundReferences: [], + originId: undefined, + spaces: ['default', 'another-space'], + spacesWithMatchingAliases: ['space-1', 'space-2', 'space-3', 'space-4'], + spacesWithMatchingOrigins: undefined, + type: 'type-a', + }, + { + id: 'id-2', + inboundReferences: [], + originId: undefined, + spaces: ['default', 'another-space'], + spacesWithMatchingAliases: undefined, + spacesWithMatchingOrigins: undefined, + type: 'type-a', + }, + { + id: 'id-3', + inboundReferences: [{ id: 'id-1', name: 'ref-name', type: 'type-a' }], + originId: undefined, + spaces: ['default', 'another-space'], + spacesWithMatchingAliases: undefined, + spacesWithMatchingOrigins: undefined, + type: 'type-a', + }, + ]; + beforeEach(() => { - params = setup([obj1, obj2], {}, mockSecurityExt); + params = setup(objects, {}, mockSecurityExt); mockMgetResults({ found: true, references: [obj3] }, { found: true, references: [] }); // results for obj1 and obj2 mockMgetResults({ found: true, references: [] }); // results for obj3 mockFindLegacyUrlAliases.mockResolvedValue( @@ -487,71 +512,46 @@ describe('collectMultiNamespaceReferences', () => { }); afterEach(() => { - mockSecurityExt.performAuthorization.mockReset(); - mockSecurityExt.enforceAuthorization.mockReset(); - mockSecurityExt.redactNamespaces.mockReset(); - mockSecurityExt.addAuditEvent.mockReset(); + mockSecurityExt.authorizeAndRedactMultiNamespaceReferences.mockReset(); }); describe(`errors`, () => { test(`propagates decorated error when not authorized`, async () => { // Unlike other functions, it doesn't validate the level of authorization first, so we need to - // carry on and mock the enforce function as well to create an unauthorized condition - setupPerformAuthEnforceFailure(mockSecurityExt); + // carry on and mock the security function to create an unauthorized condition + setupAuthorizeAndRedactMultiNamespaceReferenencesFailure(mockSecurityExt); await expect(collectMultiNamespaceReferences(params)).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - }); - - test(`adds audit event per object when not successful`, async () => { - // Unlike other functions, it doesn't validate the level of authorization first, so we need to - // carry on and mock the enforce function as well to create an unauthorized condition - setupPerformAuthEnforceFailure(mockSecurityExt); - - await expect(collectMultiNamespaceReferences(params)).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, - savedObject: { type: obj.type, id: obj.id }, - error: enforceError, - }); - }); + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toHaveBeenCalledTimes(1); }); }); - describe('checks privileges', () => { + describe('calls authorizeAndRedactMultiNamespaceReferences of the security extension', () => { beforeEach(() => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeAndRedactMultiNamespaceReferenencesFailure(mockSecurityExt); }); - test(`in the default state`, async () => { - await expect(collectMultiNamespaceReferences(params)).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedSpaces = new Set(['default', ...SPACES, ...obj1LegacySpaces]); - const expectedEnforceMap = new Map([[objects[0].type, new Set(['default'])]]); + test(`in the default space`, async () => { + await expect(collectMultiNamespaceReferences(params)).rejects.toThrow(enforceError); + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toHaveBeenCalledTimes(1); - const { spaces: actualSpaces, enforceMap: actualEnforceMap } = - mockSecurityExt.performAuthorization.mock.calls[0][0]; - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeAndRedactMultiNamespaceReferences.mock.calls[0][0]; + expect(actualNamespace).toEqual('default'); + expect(actualObjects).toEqual(expectedObjects); }); - test(`in a non-default state`, async () => { + test(`in a non-default space`, async () => { const namespace = 'space-X'; await expect( collectMultiNamespaceReferences({ ...params, options: { namespace } }) ).rejects.toThrow(enforceError); + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedSpaces = new Set([namespace, ...SPACES, ...obj1LegacySpaces]); - const expectedEnforceMap = new Map([[objects[0].type, new Set([namespace])]]); - const { spaces: actualSpaces, enforceMap: actualEnforceMap } = - mockSecurityExt.performAuthorization.mock.calls[0][0]; - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeAndRedactMultiNamespaceReferences.mock.calls[0][0]; + expect(actualNamespace).toEqual(namespace); + expect(actualObjects).toEqual(expectedObjects); }); test(`with purpose 'collectMultiNamespaceReferences'`, async () => { @@ -559,15 +559,13 @@ describe('collectMultiNamespaceReferences', () => { purpose: 'collectMultiNamespaceReferences', }; - setupPerformAuthEnforceFailure(mockSecurityExt); - await expect(collectMultiNamespaceReferences({ ...params, options })).rejects.toThrow( enforceError ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.performAuthorization).toBeCalledWith( + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toBeCalledWith( expect.objectContaining({ - actions: new Set(['bulk_get']), + options: { purpose: 'collectMultiNamespaceReferences' }, }) ); }); @@ -577,15 +575,13 @@ describe('collectMultiNamespaceReferences', () => { purpose: 'updateObjectsSpaces', }; - setupPerformAuthEnforceFailure(mockSecurityExt); - await expect(collectMultiNamespaceReferences({ ...params, options })).rejects.toThrow( enforceError ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.performAuthorization).toBeCalledWith( + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toBeCalledWith( expect.objectContaining({ - actions: new Set(['share_to_space']), + options: { purpose: 'updateObjectsSpaces' }, }) ); }); @@ -593,59 +589,21 @@ describe('collectMultiNamespaceReferences', () => { describe('success', () => { beforeEach(async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - await collectMultiNamespaceReferences(params); + setupAuthorizeAndRedactMultiNamespaceReferenencesSuccess(mockSecurityExt); }); - test(`calls redactNamespaces with type, spaces, and authorization map`, async () => { - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedSpaces = new Set(['default', ...SPACES, ...obj1LegacySpaces]); - const { spaces: actualSpaces } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - - const resultObjects = [obj1, obj2, obj3]; - - // enforce is called once for all objects/spaces, then once per object - expect(mockSecurityExt.enforceAuthorization).toHaveBeenCalledTimes(resultObjects.length); - const expectedTypesAndSpaces = new Map([[objects[0].type, new Set(['default'])]]); - const { typesAndSpaces: actualTypesAndSpaces } = - mockSecurityExt.enforceAuthorization.mock.calls[0][0]; - expect(setMapsAreEqual(actualTypesAndSpaces, expectedTypesAndSpaces)).toBeTruthy(); - - // Redact is called once per object, but an additional time for object 1 because it has legacy URL aliases in another set of spaces - expect(mockSecurityExt.redactNamespaces).toBeCalledTimes(resultObjects.length + 1); - const expectedRedactParams = [ - { type: obj1.type, spaces: SPACES }, - { type: obj1.type, spaces: obj1LegacySpaces }, - { type: obj2.type, spaces: SPACES }, - { type: obj3.type, spaces: SPACES }, - ]; - - expectedRedactParams.forEach((expected, i) => { - const { savedObject, typeMap } = mockSecurityExt.redactNamespaces.mock.calls[i][0]; - expect(savedObject).toEqual( - expect.objectContaining({ - type: expected.type, - namespaces: expected.spaces, - }) - ); - expect(typeMap).toBe(authMap); - }); + // Note: this test doesn't seem particularly useful as it only verifies the mock passthrough, but + // I am not sure what else can be done at this level now that the extension handles everything + test(`returns a result when successful`, async () => { + const result = await collectMultiNamespaceReferences(params); + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).toHaveBeenCalledTimes(1); + expect(result.objects).toEqual(expectedObjects); }); + test(`returns empty array when no objects are provided`, async () => { + setupAuthorizeAndRedactMultiNamespaceReferenencesSuccess(mockSecurityExt); - test(`adds audit event per object when successful`, async () => { - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - - const resultObjects = [obj1, obj2, obj3]; - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(resultObjects.length); - resultObjects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, - savedObject: { type: obj.type, id: obj.id }, - error: undefined, - }); - }); + const result = await collectMultiNamespaceReferences({ ...params, objects: [] }); + expect(result).toEqual({ objects: [] }); + expect(mockSecurityExt.authorizeAndRedactMultiNamespaceReferences).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.ts index 183d2509ad48d..d8b25dc886f20 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/collect_multi_namespace_references.ts @@ -7,7 +7,6 @@ */ import { isNotFoundFromUnsupportedServer } from '@kbn/core-elasticsearch-server-internal'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsCollectMultiNamespaceReferencesOptions, @@ -15,11 +14,12 @@ import type { SavedObjectReferenceWithContext, } from '@kbn/core-saved-objects-api-server'; import { - AuditAction, type ISavedObjectsSecurityExtension, type ISavedObjectTypeRegistry, + type SavedObject, + SavedObjectsErrorHelpers, } from '@kbn/core-saved-objects-server'; -import { SavedObjectsErrorHelpers, SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; import { type SavedObjectsSerializer, getObjectKey, @@ -72,7 +72,7 @@ export interface CollectMultiNamespaceReferencesParams { export async function collectMultiNamespaceReferences( params: CollectMultiNamespaceReferencesParams ): Promise { - const { createPointInTimeFinder, objects } = params; + const { createPointInTimeFinder, objects, securityExtension, options } = params; if (!objects.length) { return { objects: [] }; } @@ -123,10 +123,16 @@ export async function collectMultiNamespaceReferences( return { ...obj, spacesWithMatchingAliases, spacesWithMatchingOrigins }; }); + if (!securityExtension) return { objects: results }; + // Now that we have *all* information for the object graph, if the Security extension is enabled, we can: check/enforce authorization, // write audit events, filter the object graph, and redact spaces from the objects. - const filteredAndRedactedResults = await optionallyUseSecurity(results, params); - + const filteredAndRedactedResults = + await securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace: SavedObjectsUtils.namespaceIdToString(options?.namespace), + objects: results, + options: { purpose: options?.purpose }, + }); return { objects: filteredAndRedactedResults, }; @@ -220,196 +226,3 @@ async function getObjectsAndReferences({ return { objectMap, inboundReferencesMap }; } - -/** - * Checks/enforces authorization, writes audit events, filters the object graph, and redacts spaces from the share_to_space/bulk_get - * response. In other SavedObjectsRepository functions we do this before decrypting attributes. However, because of the - * share_to_space/bulk_get response logic involved in deciding between the exact match or alias match, it's cleaner to do authorization, - * auditing, filtering, and redaction all afterwards. - */ -async function optionallyUseSecurity( - objectsWithContext: SavedObjectReferenceWithContext[], - params: CollectMultiNamespaceReferencesParams -) { - const { securityExtension, objects, options = {} } = params; - const { purpose, namespace } = options; - const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); - if (!securityExtension) { - return objectsWithContext; - } - - // Check authorization based on all *found* object types / spaces - const typesToAuthorize = new Set(); - const spacesToAuthorize = new Set([namespaceString]); - const addSpacesToAuthorize = (spaces: string[] = []) => { - for (const space of spaces) spacesToAuthorize.add(space); - }; - for (const obj of objectsWithContext) { - typesToAuthorize.add(obj.type); - addSpacesToAuthorize(obj.spaces); - addSpacesToAuthorize(obj.spacesWithMatchingAliases); - addSpacesToAuthorize(obj.spacesWithMatchingOrigins); - } - const action = - purpose === 'updateObjectsSpaces' ? ('share_to_space' as const) : ('bulk_get' as const); - - // Enforce authorization based on all *requested* object types and the current space - const typesAndSpaces = objects.reduce( - (acc, { type }) => (acc.has(type) ? acc : acc.set(type, new Set([namespaceString]))), // Always enforce authZ for the active space - new Map>() - ); - - const { typeMap } = await securityExtension?.performAuthorization({ - actions: new Set([action]), - types: typesToAuthorize, - spaces: spacesToAuthorize, - enforceMap: typesAndSpaces, - auditCallback: (error) => { - if (!error) return; // We will audit success results below, after redaction - for (const { type, id } of objects) { - securityExtension!.addAuditEvent({ - action: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, - savedObject: { type, id }, - error, - }); - } - }, - }); - - // Now, filter/redact the results. Most SOR functions just redact the `namespaces` field from each returned object. However, this function - // will actually filter the returned object graph itself. - // This is done in two steps: (1) objects which the user can't access *in this space* are filtered from the graph, and the - // graph is rearranged to avoid leaking information. (2) any spaces that the user can't access are redacted from each individual object. - // After we finish filtering, we can write audit events for each object that is going to be returned to the user. - const requestedObjectsSet = objects.reduce( - (acc, { type, id }) => acc.add(`${type}:${id}`), - new Set() - ); - const retrievedObjectsSet = objectsWithContext.reduce( - (acc, { type, id }) => acc.add(`${type}:${id}`), - new Set() - ); - const traversedObjects = new Set(); - const filteredObjectsMap = new Map(); - const getIsAuthorizedForInboundReference = (inbound: { type: string; id: string }) => { - const found = filteredObjectsMap.get(`${inbound.type}:${inbound.id}`); - return found && !found.isMissing; // If true, this object can be linked back to one of the requested objects - }; - let objectsToProcess = [...objectsWithContext]; - while (objectsToProcess.length > 0) { - const obj = objectsToProcess.shift()!; - const { type, id, spaces, inboundReferences } = obj; - const objKey = `${type}:${id}`; - traversedObjects.add(objKey); - // Is the user authorized to access this object in this space? - let isAuthorizedForObject = true; - try { - // ToDo: this is the only remaining call to enforceAuthorization outside of the security extension - // This was a bit complicated to change now, but can ultimately be removed when authz logic is - // migrated from the repo level to the extension level. - securityExtension.enforceAuthorization({ - typesAndSpaces: new Map([[type, new Set([namespaceString])]]), - action, - typeMap, - }); - } catch (err) { - isAuthorizedForObject = false; - } - // Redact the inbound references so we don't leak any info about other objects that the user is not authorized to access - const redactedInboundReferences = inboundReferences.filter((inbound) => { - if (inbound.type === type && inbound.id === id) { - // circular reference, don't redact it - return true; - } - return getIsAuthorizedForInboundReference(inbound); - }); - // If the user is not authorized to access at least one inbound reference of this object, then we should omit this object. - const isAuthorizedForGraph = - requestedObjectsSet.has(objKey) || // If true, this is one of the requested objects, and we checked authorization above - redactedInboundReferences.some(getIsAuthorizedForInboundReference); - - if (isAuthorizedForObject && isAuthorizedForGraph) { - if (spaces.length) { - // Only generate success audit records for "non-empty results" with 1+ spaces - // ("empty result" means the object was a non-multi-namespace type, or hidden type, or not found) - securityExtension.addAuditEvent({ - action: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, - savedObject: { type, id }, - }); - } - filteredObjectsMap.set(objKey, obj); - } else if (!isAuthorizedForObject && isAuthorizedForGraph) { - filteredObjectsMap.set(objKey, { ...obj, spaces: [], isMissing: true }); - } else if (isAuthorizedForObject && !isAuthorizedForGraph) { - const hasUntraversedInboundReferences = inboundReferences.some( - (ref) => - !traversedObjects.has(`${ref.type}:${ref.id}`) && - retrievedObjectsSet.has(`${ref.type}:${ref.id}`) - ); - - if (hasUntraversedInboundReferences) { - // this object has inbound reference(s) that we haven't traversed yet; bump it to the back of the list - objectsToProcess = [...objectsToProcess, obj]; - } else { - // There should never be a missing inbound reference. - // If there is, then something has gone terribly wrong. - const missingInboundReference = inboundReferences.find( - (ref) => - !traversedObjects.has(`${ref.type}:${ref.id}`) && - !retrievedObjectsSet.has(`${ref.type}:${ref.id}`) - ); - - if (missingInboundReference) { - throw new Error( - `Unexpected inbound reference to "${missingInboundReference.type}:${missingInboundReference.id}"` - ); - } - } - } - } - - const filteredAndRedactedObjects = [ - ...filteredObjectsMap.values(), - ].map((obj) => { - const { - type, - id, - spaces, - spacesWithMatchingAliases, - spacesWithMatchingOrigins, - inboundReferences, - } = obj; - // Redact the inbound references so we don't leak any info about other objects that the user is not authorized to access - const redactedInboundReferences = inboundReferences.filter((inbound) => { - if (inbound.type === type && inbound.id === id) { - // circular reference, don't redact it - return true; - } - return getIsAuthorizedForInboundReference(inbound); - }); - - /** Simple wrapper for the `redactNamespaces` function that expects a saved object in its params. */ - const getRedactedSpaces = (spacesArray: string[] | undefined) => { - if (!spacesArray) return; - const savedObject = { type, namespaces: spacesArray } as SavedObject; // Other SavedObject attributes aren't required - const result = securityExtension.redactNamespaces({ savedObject, typeMap }); - return result.namespaces; - }; - const redactedSpaces = getRedactedSpaces(spaces)!; - const redactedSpacesWithMatchingAliases = getRedactedSpaces(spacesWithMatchingAliases); - const redactedSpacesWithMatchingOrigins = getRedactedSpaces(spacesWithMatchingOrigins); - return { - ...obj, - spaces: redactedSpaces, - ...(redactedSpacesWithMatchingAliases && { - spacesWithMatchingAliases: redactedSpacesWithMatchingAliases, - }), - ...(redactedSpacesWithMatchingOrigins && { - spacesWithMatchingOrigins: redactedSpacesWithMatchingOrigins, - }), - inboundReferences: redactedInboundReferences, - }; - }); - - return filteredAndRedactedObjects; -} diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.test.ts index 8290f7345190d..6516fe39f016e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.test.ts @@ -8,7 +8,7 @@ import { errors as esErrors } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { decorateEsError } from './decorate_es_error'; describe('savedObjectsClient/decorateEsError', () => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.ts index 9cfdffc13a5dd..cc1d18fc7aebd 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/decorate_es_error.ts @@ -10,7 +10,7 @@ import { get } from 'lodash'; import { errors as esErrors } from '@elastic/elasticsearch'; import type { ElasticsearchErrorDetails } from '@kbn/es-errors'; import { isSupportedEsServer } from '@kbn/core-elasticsearch-server-internal'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; const responseErrors = { isServiceUnavailable: (statusCode?: number) => statusCode === 503, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/filter_utils.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/filter_utils.ts index ae7bb08039850..1cf27251a1549 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/filter_utils.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/filter_utils.ts @@ -9,7 +9,7 @@ import { set } from '@kbn/safer-lodash-set'; import { get, cloneDeep } from 'lodash'; import * as esKuery from '@kbn/es-query'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; type KueryNode = any; diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.test.ts index cd888ddb4c2e4..232c19fa7a840 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.test.ts @@ -13,17 +13,11 @@ import { } from './internal_bulk_resolve.test.mock'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsBulkResolveObject, SavedObjectsBaseOptions, } from '@kbn/core-saved-objects-api-server'; -import { - setMapsAreEqual, - SavedObjectsErrorHelpers, - SavedObjectsUtils, - setsAreEqual, -} from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; import { SavedObjectsSerializer, LEGACY_URL_ALIAS_TYPE, @@ -32,17 +26,16 @@ import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { internalBulkResolve, type InternalBulkResolveParams } from './internal_bulk_resolve'; import { normalizeNamespace } from './internal_utils'; import { - AuditAction, type ISavedObjectsEncryptionExtension, type ISavedObjectsSecurityExtension, type ISavedObjectTypeRegistry, + type SavedObject, + SavedObjectsErrorHelpers, } from '@kbn/core-saved-objects-server'; import { - authMap, enforceError, - setupPerformAuthFullyAuthorized, - setupPerformAuthEnforceFailure, - setupRedactPassthrough, + setupAuthorizeAndRedactInternalBulkResolveFailure, + setupAuthorizeAndRedactInternalBulkResolveSuccess, } from '../test_helpers/repository.test.common'; import { savedObjectsExtensionsMock } from '../mocks/saved_objects_extensions.mock'; @@ -437,6 +430,17 @@ describe('internalBulkResolve', () => { let mockSecurityExt: jest.Mocked; let params: InternalBulkResolveParams; + const expectedObjects = [ + expect.objectContaining({ + outcome: 'exactMatch', + saved_object: expect.objectContaining({ id: objects[0].id }), + }), + expect.objectContaining({ + outcome: 'exactMatch', + saved_object: expect.objectContaining({ id: objects[1].id }), + }), + ]; + beforeEach(() => { mockGetSavedObjectFromSource.mockReset(); mockGetSavedObjectFromSource.mockImplementation((_registry, type, id) => { @@ -465,110 +469,58 @@ describe('internalBulkResolve', () => { }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - + setupAuthorizeAndRedactInternalBulkResolveFailure(mockSecurityExt); await expect(internalBulkResolve(params)).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeAndRedactInternalBulkResolve).toHaveBeenCalledTimes(1); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); + test(`returns result when successful`, async () => { + setupAuthorizeAndRedactInternalBulkResolveSuccess(mockSecurityExt); const result = await internalBulkResolve(params); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeAndRedactInternalBulkResolve).toHaveBeenCalledTimes(1); const bulkIds = objects.map((obj) => obj.id); const expectedNamespaceString = SavedObjectsUtils.namespaceIdToString(namespace); expectBulkArgs(expectedNamespaceString, bulkIds); const mgetIds = bulkIds; expectMgetArgs(namespace, mgetIds); - expect(result.resolved_objects).toEqual([ - expect.objectContaining({ - outcome: 'exactMatch', - saved_object: expect.objectContaining({ id: objects[0].id }), - }), - expect.objectContaining({ - outcome: 'exactMatch', - saved_object: expect.objectContaining({ id: objects[1].id }), - }), - ]); + expect(result.resolved_objects).toEqual(expectedObjects); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns empty array when no objects are provided`, async () => { + setupAuthorizeAndRedactInternalBulkResolveSuccess(mockSecurityExt); - await internalBulkResolve(params); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_get']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([objects[0].type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(objects[0].type, new Set([namespace])); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + const result = await internalBulkResolve({ ...params, objects: [] }); + expect(result).toEqual({ resolved_objects: [] }); + expect(mockSecurityExt.authorizeAndRedactInternalBulkResolve).not.toHaveBeenCalled(); }); - test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - - await internalBulkResolve(params); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - - expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj, i) => { - const { savedObject, typeMap } = mockSecurityExt.redactNamespaces.mock.calls[i][0]; - expect(savedObject).toEqual( - expect.objectContaining({ - type: obj.type, - id: obj.id, - namespaces: [namespace], - }) - ); - expect(typeMap).toBe(authMap); + describe('calls authorizeAndRedactInternalBulkResolve of the security extension', () => { + beforeEach(() => { + setupAuthorizeAndRedactInternalBulkResolveFailure(mockSecurityExt); }); - }); - test(`adds audit event per object when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`in the default space`, async () => { + await expect( + internalBulkResolve({ ...params, options: { namespace: 'default' } }) + ).rejects.toThrow(enforceError); + expect(mockSecurityExt.authorizeAndRedactInternalBulkResolve).toHaveBeenCalledTimes(1); - await internalBulkResolve(params); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.RESOLVE, - savedObject: { type: obj.type, id: obj.id }, - error: undefined, - }); + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeAndRedactInternalBulkResolve.mock.calls[0][0]; + expect(actualNamespace).toBeUndefined(); + expect(actualObjects).toEqual(expectedObjects); }); - }); - test(`adds audit event per object when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - await expect(internalBulkResolve(params)).rejects.toThrow(enforceError); + test(`in a non-default space`, async () => { + await expect(internalBulkResolve(params)).rejects.toThrow(enforceError); + expect(mockSecurityExt.authorizeAndRedactInternalBulkResolve).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.RESOLVE, - savedObject: { type: obj.type, id: obj.id }, - error: enforceError, - }); + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeAndRedactInternalBulkResolve.mock.calls[0][0]; + expect(actualNamespace).toEqual(namespace); + expect(actualObjects).toEqual(expectedObjects); }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.ts index 2d4e75654b1fe..3b2e934854a52 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve.ts @@ -9,7 +9,6 @@ import type { MgetResponseItem } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isNotFoundFromUnsupportedServer } from '@kbn/core-elasticsearch-server-internal'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsBaseOptions, SavedObjectsBulkResolveObject, @@ -18,17 +17,14 @@ import type { SavedObjectsIncrementCounterOptions, } from '@kbn/core-saved-objects-api-server'; import { - AuditAction, type ISavedObjectsEncryptionExtension, type ISavedObjectsSecurityExtension, type ISavedObjectTypeRegistry, type SavedObjectsRawDocSource, -} from '@kbn/core-saved-objects-server'; -import { + type SavedObject, + type BulkResolveError, SavedObjectsErrorHelpers, - SavedObjectsUtils, - type DecoratedError, -} from '@kbn/core-saved-objects-utils-server'; +} from '@kbn/core-saved-objects-server'; import { LEGACY_URL_ALIAS_TYPE, type LegacyUrlAlias, @@ -83,25 +79,14 @@ export interface InternalBulkResolveParams { * @public */ export interface InternalSavedObjectsBulkResolveResponse { - resolved_objects: Array | InternalBulkResolveError>; -} - -/** - * Error result for the internal bulkResolve function. - * - * @internal - */ -export interface InternalBulkResolveError { - type: string; - id: string; - error: DecoratedError; + resolved_objects: Array | BulkResolveError>; } /** Type guard used in the repository. */ export function isBulkResolveError( - result: SavedObjectsResolveResponse | InternalBulkResolveError -): result is InternalBulkResolveError { - return !!(result as InternalBulkResolveError).error; + result: SavedObjectsResolveResponse | BulkResolveError +): result is BulkResolveError { + return !!(result as BulkResolveError).error; } type AliasInfo = Pick; @@ -201,9 +186,7 @@ export async function internalBulkResolve( } // map function for pMap below - const mapper = async ( - either: Either - ) => { + const mapper = async (either: Either) => { if (isLeft(either)) { return either.value; } @@ -271,92 +254,18 @@ export async function internalBulkResolve( { refresh: false } ).catch(() => {}); // if the call fails for some reason, intentionally swallow the error - const redacted = await authorizeAuditAndRedact(resolvedObjects, securityExtension, namespace); - return { resolved_objects: redacted }; -} - -/** - * Checks authorization, writes audit events, and redacts namespaces from the bulkResolve response. In other SavedObjectsRepository - * functions we do this before decrypting attributes. However, because of the bulkResolve logic involved in deciding between the exact match - * or alias match, it's cleaner to do authorization, auditing, and redaction all afterwards. - */ -async function authorizeAuditAndRedact( - resolvedObjects: Array | InternalBulkResolveError>, - securityExtension: ISavedObjectsSecurityExtension | undefined, - namespace: string | undefined -) { - if (!securityExtension) { - return resolvedObjects; - } - - const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); - const typesAndSpaces = new Map>(); - const spacesToAuthorize = new Set(); - const auditableObjects: Array<{ type: string; id: string }> = []; + if (!securityExtension) return { resolved_objects: resolvedObjects }; - for (const result of resolvedObjects) { - let auditableObject: { type: string; id: string } | undefined; - if (isBulkResolveError(result)) { - const { type, id, error } = result; - if (!SavedObjectsErrorHelpers.isBadRequestError(error)) { - // Only "not found" errors should show up as audit events (not "unsupported type" errors) - auditableObject = { type, id }; - } - } else { - const { type, id, namespaces = [] } = result.saved_object; - auditableObject = { type, id }; - for (const space of namespaces) { - spacesToAuthorize.add(space); - } - } - if (auditableObject) { - auditableObjects.push(auditableObject); - const spacesToEnforce = - typesAndSpaces.get(auditableObject.type) ?? new Set([namespaceString]); // Always enforce authZ for the active space - spacesToEnforce.add(namespaceString); - typesAndSpaces.set(auditableObject.type, spacesToEnforce); - spacesToAuthorize.add(namespaceString); - } - } - - if (typesAndSpaces.size === 0) { - // We only had "unsupported type" errors, there are no types to check privileges for, just return early - return resolvedObjects; - } - - const authorizationResult = await securityExtension?.performAuthorization({ - actions: new Set(['bulk_get']), - types: new Set(typesAndSpaces.keys()), - spaces: spacesToAuthorize, - enforceMap: typesAndSpaces, - auditCallback: (error) => { - for (const { type, id } of auditableObjects) { - securityExtension.addAuditEvent({ - action: AuditAction.RESOLVE, - savedObject: { type, id }, - error, - }); - } - }, - }); - - return resolvedObjects.map((result) => { - if (isBulkResolveError(result)) { - return result; - } - return { - ...result, - saved_object: securityExtension.redactNamespaces({ - typeMap: authorizationResult.typeMap, - savedObject: result.saved_object, - }), - }; + const redactedObjects = await securityExtension?.authorizeAndRedactInternalBulkResolve({ + namespace, + objects: resolvedObjects, }); + return { resolved_objects: redactedObjects }; } /** Separates valid and invalid object types */ function validateObjectTypes(objects: SavedObjectsBulkResolveObject[], allowedTypes: string[]) { - return objects.map>((object) => { + return objects.map>((object) => { const { type, id } = object; if (!allowedTypes.includes(type)) { return { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts index 37b29a6a94e66..7d1e8005b75ee 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/internal_utils.ts @@ -7,17 +7,14 @@ */ import type { Payload } from '@hapi/boom'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; -import type { - ISavedObjectTypeRegistry, - SavedObjectsRawDoc, - SavedObjectsRawDocSource, -} from '@kbn/core-saved-objects-server'; import { + type ISavedObjectTypeRegistry, + type SavedObjectsRawDoc, + type SavedObjectsRawDocSource, + type SavedObject, SavedObjectsErrorHelpers, - SavedObjectsUtils, - ALL_NAMESPACES_STRING, -} from '@kbn/core-saved-objects-utils-server'; +} from '@kbn/core-saved-objects-server'; +import { SavedObjectsUtils, ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; import { decodeRequestVersion, encodeHitVersion, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/preflight_check_for_create.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/preflight_check_for_create.ts index 1daa7b5f37ea4..ae09b0e1e4228 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/preflight_check_for_create.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/preflight_check_for_create.ts @@ -8,15 +8,13 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isNotFoundFromUnsupportedServer } from '@kbn/core-elasticsearch-server-internal'; -import type { - ISavedObjectTypeRegistry, - SavedObjectsRawDoc, - SavedObjectsRawDocSource, -} from '@kbn/core-saved-objects-server'; import { + type ISavedObjectTypeRegistry, + type SavedObjectsRawDoc, + type SavedObjectsRawDocSource, SavedObjectsErrorHelpers, - ALL_NAMESPACES_STRING, -} from '@kbn/core-saved-objects-utils-server'; +} from '@kbn/core-saved-objects-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; import { LEGACY_URL_ALIAS_TYPE, getObjectKey, diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.security_extension.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.security_extension.test.ts index bd30f9a10eeb1..71788876b4309 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.security_extension.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.security_extension.test.ts @@ -20,18 +20,12 @@ import { estypes } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { SavedObjectsBulkUpdateObject } from '@kbn/core-saved-objects-api-server'; import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; -import { SavedObject } from '@kbn/core-saved-objects-server'; import { ISavedObjectsSecurityExtension, - AuditAction, SavedObjectsRawDocSource, AuthorizationTypeEntry, + SavedObject, } from '@kbn/core-saved-objects-server'; -import { - setMapsAreEqual, - arrayMapsAreEqual, - setsAreEqual, -} from '@kbn/core-saved-objects-utils-server'; import { kibanaMigratorMock } from '../mocks'; import { createRegistry, @@ -48,7 +42,6 @@ import { updateSuccess, deleteSuccess, removeReferencesToSuccess, - REMOVE_REFS_COUNT, checkConflictsSuccess, findSuccess, mockTimestampFields, @@ -63,12 +56,14 @@ import { expectUpdateResult, bulkDeleteSuccess, createBulkDeleteSuccessStatus, - setupPerformAuthFullyAuthorized, - setupPerformAuthPartiallyAuthorized, - setupPerformAuthUnauthorized, - setupPerformAuthEnforceFailure, + REMOVE_REFS_COUNT, + setupGetFindRedactTypeMap, + generateIndexPatternSearchResults, + setupAuthorizeFunc, + setupAuthorizeFind, } from '../test_helpers/repository.test.common'; import { savedObjectsExtensionsMock } from '../mocks/saved_objects_extensions.mock'; +import { arrayMapsAreEqual } from '@kbn/core-saved-objects-utils-server'; // BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository // so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. @@ -124,45 +119,56 @@ describe('SavedObjectsRepository Security Extension', () => { mockSecurityExt = savedObjectsExtensionsMock.createSecurityExtension(); mockGetCurrentTime.mockReturnValue(mockTimestamp); repository = instantiateRepository(); + setupGetFindRedactTypeMap(mockSecurityExt); }); afterEach(() => { - mockSecurityExt.performAuthorization.mockClear(); mockSecurityExt.redactNamespaces.mockClear(); mockGetSearchDsl.mockClear(); }); describe('#get', () => { - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeGet rejects promise`, async () => { + mockSecurityExt.authorizeGet.mockRejectedValueOnce(checkAuthError); await expect( getSuccess(client, repository, registry, type, id, { namespace }) ).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeGet, 'unauthorized'); await expect( getSuccess(client, repository, registry, type, id, { namespace }) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeGet, 'partially_authorized'); + setupRedactPassthrough(mockSecurityExt); + + const result = await getSuccess(client, repository, registry, type, id, { namespace }); + + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledTimes(1); + expect(client.get).toHaveBeenCalledTimes(1); + expect(result).toEqual(expect.objectContaining({ type, id, namespaces: [namespace] })); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeGet, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const result = await getSuccess(client, repository, registry, type, id, { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledTimes(1); expect(client.get).toHaveBeenCalledTimes(1); expect(result).toEqual(expect.objectContaining({ type, id, namespaces: [namespace] })); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { + test(`calls authorizeGet with correct parameters`, async () => { await getSuccess( client, repository, @@ -176,35 +182,25 @@ describe('SavedObjectsRepository Security Extension', () => { multiNamespaceObjNamespaces // all of the object's namespaces from preflight check are added to the auth check call ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['get']); - const expectedSpaces = new Set(multiNamespaceObjNamespaces); - const expectedTypes = new Set([MULTI_NAMESPACE_CUSTOM_INDEX_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, new Set([namespace])); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledWith({ + namespace, + object: { + existingNamespaces: multiNamespaceObjNamespaces, + id, + type: MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, + }, + objectNotFound: false, + }); }); test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeGet, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); await getSuccess(client, repository, registry, type, id, { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledWith( expect.objectContaining({ @@ -213,71 +209,57 @@ describe('SavedObjectsRepository Security Extension', () => { }) ); }); - - test(`adds audit event when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - - await getSuccess(client, repository, registry, type, id, { namespace }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.GET, - savedObject: { type, id }, - }); - }); - - test(`adds audit event when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - await expect( - getSuccess(client, repository, registry, type, id, { namespace }) - ).rejects.toThrow(enforceError); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.GET, - savedObject: { type, id }, - error: enforceError, - }); - }); }); describe('#update', () => { - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeUpdate rejects promise`, async () => { + mockSecurityExt.authorizeUpdate.mockRejectedValueOnce(checkAuthError); await expect( updateSuccess(client, repository, registry, type, id, attributes, { namespace }) ).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'unauthorized'); await expect( updateSuccess(client, repository, registry, type, id, attributes, { namespace }) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'partially_authorized'); + setupRedactPassthrough(mockSecurityExt); + + const result = await updateSuccess(client, repository, registry, type, id, attributes, { + namespace, + }); + + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledTimes(1); + expect(client.update).toHaveBeenCalledTimes(1); + expect(result).toEqual( + expect.objectContaining({ id, type, attributes, namespaces: [namespace] }) + ); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const result = await updateSuccess(client, repository, registry, type, id, attributes, { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledTimes(1); expect(client.update).toHaveBeenCalledTimes(1); expect(result).toEqual( expect.objectContaining({ id, type, attributes, namespaces: [namespace] }) ); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { + test(`calls authorizeUpdate with correct parameters`, async () => { await updateSuccess( client, repository, @@ -292,35 +274,28 @@ describe('SavedObjectsRepository Security Extension', () => { multiNamespaceObjNamespaces // all of the object's namespaces from preflight check are added to the auth check call ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['update']); - const expectedSpaces = new Set(multiNamespaceObjNamespaces); - const expectedTypes = new Set([MULTI_NAMESPACE_CUSTOM_INDEX_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, new Set([namespace])); + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledTimes(1); + const expectedNamespace = namespace; + const expectedObject = { + type: 'multiNamespaceTypeCustomIndex', + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + existingNamespaces: multiNamespaceObjNamespaces, + }; - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + const { namespace: actualNamespace, object: actualObject } = + mockSecurityExt.authorizeUpdate.mock.calls[0][0]; - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(actualNamespace).toEqual(expectedNamespace); + expect(actualObject).toEqual(expectedObject); }); test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); await updateSuccess(client, repository, registry, type, id, attributes, { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledWith( expect.objectContaining({ @@ -329,65 +304,35 @@ describe('SavedObjectsRepository Security Extension', () => { }) ); }); - - test(`adds audit event when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - - await updateSuccess(client, repository, registry, type, id, attributes, { namespace }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE, - savedObject: { type, id }, - error: undefined, - outcome: 'unknown', - }); - }); - test(`adds audit event when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - await expect( - updateSuccess(client, repository, registry, type, id, { namespace }) - ).rejects.toThrow(enforceError); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE, - savedObject: { type, id }, - error: enforceError, - }); - }); }); describe('#create', () => { - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeCreate rejects promise`, async () => { + mockSecurityExt.authorizeCreate.mockRejectedValueOnce(checkAuthError); await expect(repository.create(type, attributes, { namespace })).rejects.toThrow( checkAuthError ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - + setupAuthorizeFunc(mockSecurityExt.authorizeCreate, 'unauthorized'); await expect(repository.create(type, attributes, { namespace })).rejects.toThrow( enforceError ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeCreate, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const result = await repository.create(type, attributes, { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); expect(client.create).toHaveBeenCalledTimes(1); expect(result).toEqual( expect.objectContaining({ @@ -399,71 +344,75 @@ describe('SavedObjectsRepository Security Extension', () => { ); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - await repository.create(MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, attributes, { + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeCreate as jest.Mock, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); + + const result = await repository.create(type, attributes, { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['create']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([MULTI_NAMESPACE_CUSTOM_INDEX_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, new Set([namespace])); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); + expect(client.create).toHaveBeenCalledTimes(1); + expect(result).toEqual( + expect.objectContaining({ + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + type, + attributes, + namespaces: [namespace], + }) + ); + }); - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + test(`calls authorizeCreate with correct parameters`, async () => { + await repository.create(MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, attributes, { + namespace, + }); - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); + const expectedNamespace = namespace; + const expectedObject = { + type: 'multiNamespaceTypeCustomIndex', + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: undefined, + existingNamespaces: [], + }; + const { namespace: actualNamespace, object: actualObject } = + mockSecurityExt.authorizeCreate.mock.calls[0][0]; + + expect(actualNamespace).toEqual(expectedNamespace); + expect(actualObject).toEqual(expectedObject); }); - test(`calls performAuthorization with initial namespaces`, async () => { + test(`calls authorizeCreate with initial namespaces`, async () => { await repository.create(MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, attributes, { namespace, initialNamespaces: multiNamespaceObjNamespaces, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['create']); - const expectedSpaces = new Set(multiNamespaceObjNamespaces); - const expectedTypes = new Set([MULTI_NAMESPACE_CUSTOM_INDEX_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set( - MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, - new Set(multiNamespaceObjNamespaces) - ); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); + const expectedNamespace = namespace; + const expectedObject = { + type: 'multiNamespaceTypeCustomIndex', + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: multiNamespaceObjNamespaces, + existingNamespaces: [], + }; - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + const { namespace: actualNamespace, object: actualObject } = + mockSecurityExt.authorizeCreate.mock.calls[0][0]; - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); + expect(actualNamespace).toEqual(expectedNamespace); + expect(actualObject).toEqual(expectedObject); }); test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeCreate as jest.Mock, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); await repository.create(type, attributes, { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledWith( @@ -477,42 +426,6 @@ describe('SavedObjectsRepository Security Extension', () => { }) ); }); - - test(`adds audit event when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - - await repository.create(type, attributes, { namespace }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.CREATE, - savedObject: { - type, - id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), - }, - error: undefined, - outcome: 'unknown', - }); - }); - - test(`adds audit event when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - await expect(repository.create(type, attributes, { namespace })).rejects.toThrow( - enforceError - ); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.CREATE, - savedObject: { - type, - id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), - }, - error: enforceError, - }); - }); }); describe('#delete', () => { @@ -524,178 +437,115 @@ describe('SavedObjectsRepository Security Extension', () => { mockDeleteLegacyUrlAliases.mockClear(); }); - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeDelete rejects promise`, async () => { + mockSecurityExt.authorizeDelete.mockRejectedValueOnce(checkAuthError); await expect( deleteSuccess(client, repository, registry, type, id, { namespace }) ).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeDelete, 'unauthorized'); await expect( deleteSuccess(client, repository, registry, type, id, { namespace }) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledTimes(1); }); - test(`returns empty object result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns empty object result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeDelete, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const result = await deleteSuccess(client, repository, registry, type, id, { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledTimes(1); expect(client.delete).toHaveBeenCalledTimes(1); expect(result).toEqual({}); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, id, { - namespace, - force: true, - }); - - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['delete']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([MULTI_NAMESPACE_CUSTOM_INDEX_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, new Set([namespace])); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); - }); - - test(`adds audit event when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns empty object result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeDelete, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); - await deleteSuccess(client, repository, registry, type, id, { namespace }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.DELETE, - savedObject: { type, id }, - error: undefined, - outcome: 'unknown', + const result = await deleteSuccess(client, repository, registry, type, id, { + namespace, }); - }); - test(`adds audit event when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledTimes(1); + expect(client.delete).toHaveBeenCalledTimes(1); + expect(result).toEqual({}); + }); - await expect( - deleteSuccess(client, repository, registry, type, id, { namespace }) - ).rejects.toThrow(enforceError); + test(`calls authorizeDelete with correct parameters`, async () => { + await deleteSuccess(client, repository, registry, type, id, { + namespace, + force: true, + }); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.DELETE, - savedObject: { type, id }, - error: enforceError, + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledWith({ + namespace, + object: { type, id }, }); }); }); describe('#removeReferencesTo', () => { - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeRemoveReferences rejects promise`, async () => { + mockSecurityExt.authorizeRemoveReferences.mockRejectedValueOnce(checkAuthError); await expect( removeReferencesToSuccess(client, repository, type, id, { namespace }) ).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeRemoveReferences, 'unauthorized'); await expect( removeReferencesToSuccess(client, repository, type, id, { namespace }) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeRemoveReferences, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const result = await removeReferencesToSuccess(client, repository, type, id, { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); expect(client.updateByQuery).toHaveBeenCalledTimes(1); expect(result).toEqual(expect.objectContaining({ updated: REMOVE_REFS_COUNT })); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - await removeReferencesToSuccess(client, repository, type, id, { namespace }); - - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['delete']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(type, new Set([namespace])); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); - }); - - test(`adds audit event when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeRemoveReferences, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); - await removeReferencesToSuccess(client, repository, type, id, { namespace }); + const result = await removeReferencesToSuccess(client, repository, type, id, { namespace }); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.REMOVE_REFERENCES, - savedObject: { type, id }, - error: undefined, - outcome: 'unknown', - }); + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); + expect(client.updateByQuery).toHaveBeenCalledTimes(1); + expect(result).toEqual(expect.objectContaining({ updated: REMOVE_REFS_COUNT })); }); - test(`adds audit event when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - await expect( - removeReferencesToSuccess(client, repository, type, id, { namespace }) - ).rejects.toThrow(enforceError); + test(`calls authorizeRemoveReferences with correct parameters`, async () => { + await removeReferencesToSuccess(client, repository, type, id, { namespace }); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.REMOVE_REFERENCES, - savedObject: { type, id }, - error: enforceError, + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledWith({ + namespace, + object: { + id, + type, + }, }); }); }); @@ -704,159 +554,128 @@ describe('SavedObjectsRepository Security Extension', () => { const obj1 = { type, id: 'one' }; const obj2 = { type, id: 'two' }; - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + const expectedResult = { + errors: [ + { + error: { + error: 'Conflict', + message: `Saved object [${obj1.type}/${obj1.id}] conflict`, + statusCode: 409, + }, + id: obj1.id, + type: obj1.type, + }, + { + error: { + error: 'Conflict', + message: `Saved object [${obj2.type}/${obj2.id}] conflict`, + statusCode: 409, + }, + id: obj2.id, + type: obj2.type, + }, + ], + }; + + test(`propagates decorated error when authorizeCheckConflicts rejects promise`, async () => { + mockSecurityExt.authorizeCheckConflicts.mockRejectedValueOnce(checkAuthError); await expect( checkConflictsSuccess(client, repository, registry, [obj1, obj2], { namespace }) ).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeCheckConflicts, 'unauthorized'); await expect( checkConflictsSuccess(client, repository, registry, [obj1, obj2], { namespace }) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledTimes(1); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeCheckConflicts, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const result = await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledTimes(1); expect(client.mget).toHaveBeenCalledTimes(1); - // Default mock mget makes each object found - expect(result).toEqual( - expect.objectContaining({ - errors: [ - { - error: { - error: 'Conflict', - message: `Saved object [${obj1.type}/${obj1.id}] conflict`, - statusCode: 409, - }, - id: obj1.id, - type: obj1.type, - }, - { - error: { - error: 'Conflict', - message: `Saved object [${obj2.type}/${obj2.id}] conflict`, - statusCode: 409, - }, - id: obj2.id, - type: obj2.type, - }, - ], - }) - ); + expect(result).toEqual(expectedResult); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { namespace }); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeCheckConflicts, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_create']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([obj1.type, obj2.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(obj1.type, new Set([namespace])); - expectedEnforceMap.set(obj2.type, new Set([namespace])); + const result = await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { + namespace, + }); - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledTimes(1); + expect(client.mget).toHaveBeenCalledTimes(1); + expect(result).toEqual(expectedResult); + }); + + test(`calls authorizeCheckConflicts with correct parameters`, async () => { + await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { namespace }); + + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledWith({ + namespace, + objects: [obj1, obj2], + }); }); }); describe('#openPointInTimeForType', () => { - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeOpenPointInTime rejects promise`, async () => { + mockSecurityExt.authorizeOpenPointInTime.mockRejectedValueOnce(checkAuthError); await expect(repository.openPointInTimeForType(type)).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeOpenPointInTime).toHaveBeenCalledTimes(1); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeOpenPointInTime, 'partially_authorized'); client.openPointInTime.mockResponseOnce({ id }); const result = await repository.openPointInTimeForType(type); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeOpenPointInTime).toHaveBeenCalledTimes(1); expect(client.openPointInTime).toHaveBeenCalledTimes(1); expect(result).toEqual(expect.objectContaining({ id })); }); - test(`adds audit event when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeOpenPointInTime, 'fully_authorized'); client.openPointInTime.mockResponseOnce({ id }); - await repository.openPointInTimeForType(type); + const result = await repository.openPointInTimeForType(type); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.OPEN_POINT_IN_TIME, - outcome: 'unknown', - }); + expect(mockSecurityExt.authorizeOpenPointInTime).toHaveBeenCalledTimes(1); + expect(client.openPointInTime).toHaveBeenCalledTimes(1); + expect(result).toEqual(expect.objectContaining({ id })); }); test(`throws an error when unauthorized`, async () => { - setupPerformAuthUnauthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeOpenPointInTime, 'unauthorized'); await expect(repository.openPointInTimeForType(type)).rejects.toThrowError(); }); - test(`adds audit event when unauthorized`, async () => { - setupPerformAuthUnauthorized(mockSecurityExt); - - await expect(repository.openPointInTimeForType(type)).rejects.toThrowError(); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.OPEN_POINT_IN_TIME, - error: new Error('User is unauthorized for any requested types/spaces.'), - }); - }); - - test(`calls performAuthorization with correct actions, types, and spaces`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`calls authorizeOpenPointInTime with correct parameters`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeOpenPointInTime, 'fully_authorized'); client.openPointInTime.mockResponseOnce({ id }); - await repository.openPointInTimeForType(type, { namespaces: [namespace] }); - - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['open_point_in_time']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([type]); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(actualEnforceMap).toBeUndefined(); - expect(actualOptions).toBeUndefined(); + const namespaces = [namespace, 'x', 'y', 'z']; + await repository.openPointInTimeForType(type, { namespaces }); + expect(mockSecurityExt.authorizeOpenPointInTime).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeOpenPointInTime).toHaveBeenCalledWith({ + namespaces: new Set(namespaces), + types: new Set([type]), + }); }); }); @@ -869,30 +688,32 @@ describe('SavedObjectsRepository Security Extension', () => { expect(result).toEqual(expectedResult); }); - test(`adds audit event`, async () => { + test(`calls auditClosePointInTime`, async () => { await repository.closePointInTime(id); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.CLOSE_POINT_IN_TIME, - outcome: 'unknown', - }); + expect(mockSecurityExt.auditClosePointInTime).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.auditClosePointInTime).toHaveBeenCalledWith(); }); }); describe('#find', () => { - test(`propagates decorated error when Authorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeFind rejects promise`, async () => { + mockSecurityExt.authorizeFind.mockRejectedValueOnce(checkAuthError); + await expect(findSuccess(client, repository, { type })).rejects.toThrow(checkAuthError); + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledTimes(1); + }); + + test(`propagates decorated error when getFindRedactTypeMap rejects promise`, async () => { + mockSecurityExt.getFindRedactTypeMap.mockRejectedValueOnce(checkAuthError); await expect(findSuccess(client, repository, { type })).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.getFindRedactTypeMap).toHaveBeenCalledTimes(1); }); - test(`returns empty result when unauthorized`, async () => { - setupPerformAuthUnauthorized(mockSecurityExt); + test(`returns empty result when preauthorization is unauthorized`, async () => { + setupAuthorizeFind(mockSecurityExt, 'unauthorized'); const result = await repository.find({ type }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledTimes(1); expect(result).toEqual( expect.objectContaining({ saved_objects: [], @@ -901,18 +722,45 @@ describe('SavedObjectsRepository Security Extension', () => { ); }); + test(`returns result when getFindRedactTypeMap is unauthorized`, async () => { + setupAuthorizeFind(mockSecurityExt, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); + + const generatedResults = generateIndexPatternSearchResults(namespace); + client.search.mockResponseOnce(generatedResults); + const result = await repository.find({ type }); + + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.getFindRedactTypeMap).toHaveBeenCalledTimes(1); + expect(result.total).toBe(4); + expect(result.saved_objects).toHaveLength(4); + generatedResults.hits.hits.forEach((doc, i) => { + expect(result.saved_objects[i]).toEqual({ + id: doc._id.replace(/(foo-namespace\:)?(index-pattern|config|globalType)\:/, ''), + type: doc._source!.type, + originId: doc._source!.originId, + ...mockTimestampFields, + version: mockVersion, + score: doc._score, + attributes: doc._source![doc._source!.type], + references: [], + namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : [namespace], + }); + }); + }); + test(`calls es search with only authorized spaces when partially authorized`, async () => { // Setup partial authorization with the specific type and space of the current test definition const authRecord: Record = { find: { authorizedSpaces: [namespace] }, }; - mockSecurityExt.performAuthorization.mockResolvedValue({ + mockSecurityExt.authorizeFind.mockResolvedValue({ status: 'partially_authorized', typeMap: Object.freeze(new Map([[type, authRecord]])), }); await findSuccess(client, repository, { type, namespaces: [namespace, 'ns-1'] }); - expect(mockGetSearchDsl.mock.calls[0].length).toBe(3); // Find success verifies this is called once, this shouyld always pass + expect(mockGetSearchDsl.mock.calls[0].length).toBe(3); // Find success verifies this is called once, this should always pass const { typeToNamespacesMap: actualMap, }: { typeToNamespacesMap: Map } = @@ -925,7 +773,7 @@ describe('SavedObjectsRepository Security Extension', () => { }); test(`returns result of es find when fully authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFind(mockSecurityExt, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const { result, generatedResults } = await findSuccess( @@ -955,7 +803,7 @@ describe('SavedObjectsRepository Security Extension', () => { }); test(`uses the authorization map when partially authorized`, async () => { - setupPerformAuthPartiallyAuthorized(mockSecurityExt); + setupAuthorizeFind(mockSecurityExt, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); await findSuccess( @@ -979,7 +827,7 @@ describe('SavedObjectsRepository Security Extension', () => { }); test(`returns result of es find when partially authorized`, async () => { - setupPerformAuthPartiallyAuthorized(mockSecurityExt); + setupAuthorizeFind(mockSecurityExt, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const { result, generatedResults } = await findSuccess( @@ -1008,47 +856,45 @@ describe('SavedObjectsRepository Security Extension', () => { }); }); - test(`calls performAuthorization with correct actions, types, and spaces`, async () => { - setupPerformAuthPartiallyAuthorized(mockSecurityExt); + test(`calls authorizeFind with correct parameters`, async () => { + setupAuthorizeFind(mockSecurityExt, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); await findSuccess(client, repository, { type, namespaces: [namespace] }, 'ns-2'); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(2); - const expectedActions = new Set(['find']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([type]); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(actualEnforceMap).toBeUndefined(); - expect(actualOptions).toBeUndefined(); + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledWith({ + namespaces: new Set([namespace]), + types: new Set(['index-pattern']), + }); + }); - const { - actions: actualActions2, - spaces: actualSpaces2, - types: actualTypes2, - enforceMap: actualEnforceMap2, - options: actualOptions2, - } = mockSecurityExt.performAuthorization.mock.calls[1][0]; + test(`calls GetFindRedactTypeMap with correct parameters`, async () => { + setupAuthorizeFind(mockSecurityExt, 'partially_authorized'); + setupRedactPassthrough(mockSecurityExt); + const { generatedResults } = await findSuccess( + client, + repository, + { type, namespaces: [namespace] }, + 'ns-2' + ); - expect(setsAreEqual(actualActions2, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces2, new Set([...expectedSpaces, 'ns-2']))).toBeTruthy(); - expect(setsAreEqual(actualTypes2, expectedTypes)).toBeTruthy(); - expect(actualEnforceMap2).toBeUndefined(); - expect(actualOptions2).toBeUndefined(); + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.getFindRedactTypeMap).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.getFindRedactTypeMap).toHaveBeenCalledWith({ + previouslyCheckedNamespaces: new Set([namespace]), + objects: generatedResults.hits.hits.map((obj) => { + return { + type: obj._source?.type, + id: obj._id.slice(obj._id.lastIndexOf(':') + 1), // find removes the space/type from the ID in the original raw doc + existingNamespaces: + obj._source?.namespaces ?? obj._source?.namespace ? [obj._source?.namespace] : [], + }; + }), + }); }); test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFind(mockSecurityExt, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const { generatedResults } = await findSuccess(client, repository, { @@ -1065,44 +911,6 @@ describe('SavedObjectsRepository Security Extension', () => { }) ); }); - - test(`adds audit per object event when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - - const { generatedResults } = await findSuccess(client, repository, { - type, - namespaces: [namespace], - }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes( - generatedResults.hits.hits.length - ); - - generatedResults.hits.hits.forEach((doc, i) => { - expect(mockSecurityExt.addAuditEvent.mock.calls[i]).toEqual([ - { - action: AuditAction.FIND, - savedObject: { - type: doc._source!.type, - id: doc._id.replace(/(foo-namespace\:)?(index-pattern|config|globalType)\:/, ''), - }, - }, - ]); - }); - }); - - test(`adds audit event when not successful`, async () => { - setupPerformAuthUnauthorized(mockSecurityExt); - - await repository.find({ type }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.FIND, - error: new Error('User is unauthorized for any requested types/spaces.'), - }); - }); }); describe('#bulkGet', () => { @@ -1134,26 +942,43 @@ describe('SavedObjectsRepository Security Extension', () => { namespaces: [namespace], }; - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + const expectedAuthObjects = [ + { + error: true, + existingNamespaces: ['default'], + objectNamespaces: ['ns-1', 'ns-2', namespace], + id: '6.0.0-alpha1', + type: 'multiNamespaceTypeCustomIndex', + }, + { + error: false, + existingNamespaces: [], + objectNamespaces: ['ns-3'], + id: 'logstash-*', + type: 'index-pattern', + }, + ]; + + test(`propagates decorated error when authorizeBulkGet rejects promise`, async () => { + mockSecurityExt.authorizeBulkGet.mockRejectedValueOnce(checkAuthError); await expect( bulkGetSuccess(client, repository, registry, [obj1, obj2], { namespace }) ).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkGet, 'unauthorized'); await expect( bulkGetSuccess(client, repository, registry, [obj1, obj2], { namespace }) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkGet, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const { result, mockResponse } = await bulkGetSuccess( @@ -1164,7 +989,7 @@ describe('SavedObjectsRepository Security Extension', () => { { namespace } ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); expect(client.mget).toHaveBeenCalledTimes(1); expect(result).toEqual({ saved_objects: [ @@ -1180,7 +1005,35 @@ describe('SavedObjectsRepository Security Extension', () => { }); }); - test(`calls performAuthorization with correct parameters in default space`, async () => { + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkGet, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); + + const { result, mockResponse } = await bulkGetSuccess( + client, + repository, + registry, + [obj1, obj2], + { namespace } + ); + + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); + expect(client.mget).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + saved_objects: [ + expectBulkGetResult( + obj1, + mockResponse.docs[0] as estypes.GetGetResult + ), + expectBulkGetResult( + obj2, + mockResponse.docs[1] as estypes.GetGetResult + ), + ], + }); + }); + + test(`calls authorizeBulkGet with correct parameters in default space`, async () => { const objA = { ...obj1, type: MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, // replace the type to a mult-namespace type for this test to be thorough @@ -1190,30 +1043,14 @@ describe('SavedObjectsRepository Security Extension', () => { await bulkGetSuccess(client, repository, registry, [objA, objB]); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_get']); - const expectedSpaces = new Set(['default', ...objA.namespaces, ...objB.namespaces]); - const expectedTypes = new Set([objA.type, objB.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(objA.type, new Set(['default', ...objA.namespaces])); - expectedEnforceMap.set(objB.type, new Set(['default', ...objB.namespaces])); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledWith({ + namespace: undefined, + objects: expectedAuthObjects, + }); }); - test(`calls performAuthorization with correct parameters in non-default space`, async () => { + test(`calls authorize with correct parameters in non-default space`, async () => { const objA = { ...obj1, type: MULTI_NAMESPACE_CUSTOM_INDEX_TYPE, // replace the type to a mult-namespace type for this test to be thorough @@ -1226,38 +1063,37 @@ describe('SavedObjectsRepository Security Extension', () => { namespace: optionsNamespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_get']); - const expectedSpaces = new Set([optionsNamespace, ...objA.namespaces, ...objB.namespaces]); - const expectedTypes = new Set([objA.type, objB.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(objA.type, new Set([optionsNamespace, ...objA.namespaces])); - expectedEnforceMap.set(objB.type, new Set([optionsNamespace, ...objB.namespaces])); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledWith({ + namespace: optionsNamespace, + objects: [ + { + error: true, + existingNamespaces: [optionsNamespace], + objectNamespaces: objA.namespaces, + id: objA.id, + type: objA.type, + }, + { + error: false, + existingNamespaces: [], + objectNamespaces: objB.namespaces, + id: objB.id, + type: objB.type, + }, + ], + }); }); test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkGet, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const objects = [obj1, obj2]; await bulkGetSuccess(client, repository, registry, objects, { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(2); objects.forEach((obj, i) => { @@ -1272,40 +1108,6 @@ describe('SavedObjectsRepository Security Extension', () => { expect(typeMap).toBe(authMap); }); }); - - test(`adds audit event per object when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - - const objects = [obj1, obj2]; - await bulkGetSuccess(client, repository, registry, objects, { namespace }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.GET, - savedObject: { type: obj.type, id: obj.id }, - }); - }); - }); - - test(`adds audit event per object when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - const objects = [obj1, obj2]; - await expect( - bulkGetSuccess(client, repository, registry, objects, { namespace }) - ).rejects.toThrow(enforceError); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.GET, - savedObject: { type: obj.type, id: obj.id }, - error: enforceError, - }); - }); - }); }); describe('#bulkCreate', () => { @@ -1330,69 +1132,84 @@ describe('SavedObjectsRepository Security Extension', () => { references: [{ name: 'ref_0', type: 'test', id: '2' }], }; - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeCreate rejects promise`, async () => { + mockSecurityExt.authorizeBulkCreate.mockRejectedValueOnce(checkAuthError); await expect(bulkCreateSuccess(client, repository, [obj1, obj2])).rejects.toThrow( checkAuthError ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate as jest.Mock, 'unauthorized'); await expect( bulkCreateSuccess(client, repository, [obj1, obj2], { namespace }) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate as jest.Mock, 'partially_authorized'); + setupRedactPassthrough(mockSecurityExt); + + const objects = [obj1, obj2]; + const result = await bulkCreateSuccess(client, repository, objects); + + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); + expect(client.bulk).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + saved_objects: objects.map((obj) => expectCreateResult(obj)), + }); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate as jest.Mock, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const objects = [obj1, obj2]; const result = await bulkCreateSuccess(client, repository, objects); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); expect(client.bulk).toHaveBeenCalledTimes(1); expect(result).toEqual({ saved_objects: objects.map((obj) => expectCreateResult(obj)), }); }); - test(`calls PerformAuthorization with correct actions, types, spaces, and enforce map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`calls authorizeCreate with correct parameters`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate as jest.Mock, 'fully_authorized'); await bulkCreateSuccess(client, repository, [obj1, obj2], { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_create']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([obj1.type, obj2.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(obj1.type, new Set([namespace])); - expectedEnforceMap.set(obj2.type, new Set([namespace])); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); + const expectedNamespace = namespace; + const expectedObjects = [ + { + type: obj1.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: undefined, + existingNamespaces: [], + }, + { + type: obj2.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: undefined, + existingNamespaces: [], + }, + ]; - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeBulkCreate.mock.calls[0][0]; - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); + expect(expectedNamespace).toEqual(actualNamespace); + expect(expectedObjects).toEqual(actualObjects); }); - test(`calls performAuthorization with initial spaces for one type`, async () => { + test(`calls authorizeCreate with initial spaces for one type`, async () => { const objA = { ...obj1, type: MULTI_NAMESPACE_TYPE, @@ -1405,42 +1222,37 @@ describe('SavedObjectsRepository Security Extension', () => { }; const optionsNamespace = 'ns-5'; - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate as jest.Mock, 'fully_authorized'); await bulkCreateSuccess(client, repository, [objA, objB], { namespace: optionsNamespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_create']); - const expectedSpaces = new Set([ - optionsNamespace, - ...objA.initialNamespaces, - ...objB.initialNamespaces, - ]); - const expectedTypes = new Set([objA.type, objB.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set( - objA.type, - new Set([optionsNamespace, ...objA.initialNamespaces, ...objB.initialNamespaces]) - ); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); + const expectedNamespace = optionsNamespace; + const expectedObjects = [ + { + type: objA.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: objA.initialNamespaces, + existingNamespaces: [], + }, + { + type: objB.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: objB.initialNamespaces, + existingNamespaces: [], + }, + ]; - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeBulkCreate.mock.calls[0][0]; - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); + expect(expectedNamespace).toEqual(actualNamespace); + expect(expectedObjects).toEqual(actualObjects); }); - test(`calls performAuthorization with initial spaces for multiple types`, async () => { + test(`calls authorizeCreate with initial spaces for multiple types`, async () => { const objA = { ...obj1, type: MULTI_NAMESPACE_TYPE, @@ -1453,48 +1265,45 @@ describe('SavedObjectsRepository Security Extension', () => { }; const optionsNamespace = 'ns-5'; - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate as jest.Mock, 'fully_authorized'); await bulkCreateSuccess(client, repository, [objA, objB], { namespace: optionsNamespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_create']); - const expectedSpaces = new Set([ - optionsNamespace, - ...objA.initialNamespaces, - ...objB.initialNamespaces, - ]); - const expectedTypes = new Set([objA.type, objB.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(objA.type, new Set([optionsNamespace, ...objA.initialNamespaces])); - expectedEnforceMap.set(objB.type, new Set([optionsNamespace, ...objB.initialNamespaces])); + const expectedNamespace = optionsNamespace; + const expectedObjects = [ + { + type: objA.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: objA.initialNamespaces, + existingNamespaces: [], + }, + { + type: objB.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + initialNamespaces: objB.initialNamespaces, + existingNamespaces: [], + }, + ]; - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeBulkCreate.mock.calls[0][0]; - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); + expect(expectedNamespace).toEqual(actualNamespace); + expect(expectedObjects).toEqual(actualObjects); }); test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate as jest.Mock, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const objects = [obj1, obj2]; await bulkCreateSuccess(client, repository, [obj1, obj2], { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(2); objects.forEach((obj, i) => { @@ -1509,40 +1318,6 @@ describe('SavedObjectsRepository Security Extension', () => { expect(typeMap).toBe(authMap); }); }); - - test(`adds audit event per object when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - - const objects = [obj1, obj2]; - await bulkCreateSuccess(client, repository, objects, { namespace }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.CREATE, - savedObject: { type: obj.type, id: obj.id }, - outcome: 'unknown', - }); - }); - }); - - test(`adds audit event per object when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - const objects = [obj1, obj2]; - await expect(bulkCreateSuccess(client, repository, objects, { namespace })).rejects.toThrow( - enforceError - ); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.CREATE, - savedObject: { type: obj.type, id: obj.id }, - error: enforceError, - }); - }); - }); }); describe('#bulkUpdate', () => { @@ -1557,69 +1332,82 @@ describe('SavedObjectsRepository Security Extension', () => { attributes: { title: 'Test Two' }, }; - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeUpdate rejects promise`, async () => { + mockSecurityExt.authorizeBulkUpdate.mockRejectedValueOnce(checkAuthError); await expect(bulkUpdateSuccess(client, repository, registry, [obj1, obj2])).rejects.toThrow( checkAuthError ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkUpdate, 'unauthorized'); await expect(bulkUpdateSuccess(client, repository, registry, [obj1, obj2])).rejects.toThrow( enforceError ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'partially_authorized'); + setupRedactPassthrough(mockSecurityExt); + + const objects = [obj1, obj2]; + const result = await bulkUpdateSuccess(client, repository, registry, objects); + + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); + expect(client.bulk).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + saved_objects: objects.map((obj) => expectUpdateResult(obj)), + }); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const objects = [obj1, obj2]; const result = await bulkUpdateSuccess(client, repository, registry, objects); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); expect(client.bulk).toHaveBeenCalledTimes(1); expect(result).toEqual({ saved_objects: objects.map((obj) => expectUpdateResult(obj)), }); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`calls authorizeBulkUpdate with correct parameters`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'fully_authorized'); await bulkUpdateSuccess(client, repository, registry, [obj1, obj2], { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_update']); - const expectedSpaces = new Set([namespace]); - const expectedTypes = new Set([obj1.type, obj2.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(obj1.type, new Set([namespace])); - expectedEnforceMap.set(obj2.type, new Set([namespace])); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); + const expectedNamespace = namespace; + const expectedObjects = [ + { + type: obj1.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + existingNamespaces: [], + }, + { + type: obj2.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + existingNamespaces: [], + }, + ]; - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeBulkUpdate.mock.calls[0][0]; - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(actualNamespace).toEqual(expectedNamespace); + expect(actualObjects).toEqual(expectedObjects); }); - test(`calls performAuthorization with object spaces`, async () => { + test(`calls authorizeBulkUpdate with object spaces`, async () => { const objA = { ...obj1, namespace: 'ns-1', // object namespace @@ -1629,43 +1417,44 @@ describe('SavedObjectsRepository Security Extension', () => { namespace: 'ns-2', // object namespace }; - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeUpdate, 'fully_authorized'); await bulkUpdateSuccess(client, repository, registry, [objA, objB], { namespace, }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_update']); - const expectedSpaces = new Set([namespace, objA.namespace, objB.namespace]); - const expectedTypes = new Set([objA.type, objB.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(objA.type, new Set([namespace, objA.namespace])); - expectedEnforceMap.set(objB.type, new Set([namespace, objB.namespace])); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); + const expectedNamespace = namespace; + const expectedObjects = [ + { + type: obj1.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + objectNamespace: 'ns-1', + existingNamespaces: [], + }, + { + type: obj2.type, + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + objectNamespace: 'ns-2', + existingNamespaces: [], + }, + ]; - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; + const { namespace: actualNamespace, objects: actualObjects } = + mockSecurityExt.authorizeBulkUpdate.mock.calls[0][0]; - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(actualNamespace).toEqual(expectedNamespace); + expect(actualObjects).toEqual(expectedObjects); }); test(`calls redactNamespaces with authorization map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkUpdate, 'fully_authorized'); setupRedactPassthrough(mockSecurityExt); const objects = [obj1, obj2]; await bulkUpdateSuccess(client, repository, registry, objects, { namespace }); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(2); objects.forEach((obj, i) => { @@ -1680,40 +1469,6 @@ describe('SavedObjectsRepository Security Extension', () => { expect(typeMap).toBe(authMap); }); }); - - test(`adds audit event per object when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - - const objects = [obj1, obj2]; - await bulkUpdateSuccess(client, repository, registry, objects, { namespace }); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE, - savedObject: { type: obj.type, id: obj.id }, - outcome: 'unknown', - }); - }); - }); - - test(`adds audit event per object when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - const objects = [obj1, obj2]; - await expect( - bulkUpdateSuccess(client, repository, registry, objects, { namespace }) - ).rejects.toThrow(enforceError); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE, - savedObject: { type: obj.type, id: obj.id }, - error: enforceError, - }); - }); - }); }); describe('#bulkDelete', () => { @@ -1750,26 +1505,26 @@ describe('SavedObjectsRepository Security Extension', () => { ], }; - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + test(`propagates decorated error when authorizeBulkDelete rejects promise`, async () => { + mockSecurityExt.authorizeBulkDelete.mockRejectedValueOnce(checkAuthError); await expect( bulkDeleteSuccess(client, repository, registry, testObjs, options) ).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledTimes(1); }); test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + setupAuthorizeFunc(mockSecurityExt.authorizeBulkDelete, 'unauthorized'); await expect( bulkDeleteSuccess(client, repository, registry, testObjs, options) ).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledTimes(1); }); - test(`returns result when authorized`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + test(`returns result when partially authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkDelete, 'partially_authorized'); setupRedactPassthrough(mockSecurityExt); const result = await bulkDeleteSuccess( @@ -1781,70 +1536,44 @@ describe('SavedObjectsRepository Security Extension', () => { internalOptions ); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledTimes(1); expect(client.bulk).toHaveBeenCalledTimes(1); expect(result).toEqual({ statuses: testObjs.map((obj) => createBulkDeleteSuccessStatus(obj)), }); }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - - await bulkDeleteSuccess(client, repository, registry, testObjs, options, internalOptions); - - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['bulk_delete']); - const exptectedSpaces = new Set(internalOptions.mockMGetResponseObjects[1].initialNamespaces); - const expectedTypes = new Set([obj1.type, obj2.type]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set(obj1.type, new Set([namespace])); - expectedEnforceMap.set(obj2.type, new Set([namespace])); - - const { - actions: actualActions, - types: actualTypes, - spaces: actualSpaces, - enforceMap: actualEnforceMap, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, exptectedSpaces)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - }); + test(`returns result when fully authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkDelete, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); - test(`adds audit event per object when successful`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); + const result = await bulkDeleteSuccess( + client, + repository, + registry, + testObjs, + options, + internalOptions + ); - const objects = [obj1, obj2]; - await bulkDeleteSuccess(client, repository, registry, objects, options); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.DELETE, - savedObject: { type: obj.type, id: obj.id }, - outcome: 'unknown', - }); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledTimes(1); + expect(client.bulk).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + statuses: testObjs.map((obj) => createBulkDeleteSuccessStatus(obj)), }); }); - test(`adds audit event per object when not successful`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); - - const objects = [obj1, obj2]; - await expect( - bulkDeleteSuccess(client, repository, registry, objects, options) - ).rejects.toThrow(enforceError); + test(`calls authorizeBulkDelete with correct actions, types, spaces, and enforce map`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkDelete, 'fully_authorized'); + await bulkDeleteSuccess(client, repository, registry, testObjs, options, internalOptions); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.DELETE, - savedObject: { type: obj.type, id: obj.id }, - error: enforceError, - }); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledWith({ + namespace, + objects: [ + { type: obj1.type, id: obj1.id, existingNamespaces: [] }, + { type: obj2.type, id: obj2.id, existingNamespaces: ['foo-namespace', 'NS-1', 'NS-2'] }, + ], }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts index adfa75a48e986..bc9c5b6e7c581 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.spaces_extension.test.ts @@ -27,13 +27,13 @@ import { SavedObjectsBulkUpdateObject, } from '@kbn/core-saved-objects-api-server'; import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; -import { SavedObject } from '@kbn/core-saved-objects-server'; import { ISavedObjectsSpacesExtension, ISavedObjectsSecurityExtension, ISavedObjectsEncryptionExtension, + SavedObject, + SavedObjectsErrorHelpers, } from '@kbn/core-saved-objects-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { kibanaMigratorMock } from '../mocks'; import { createRegistry, @@ -54,10 +54,11 @@ import { bulkCreateSuccess, bulkUpdateSuccess, findSuccess, - setupPerformAuthUnauthorized, generateIndexPatternSearchResults, bulkDeleteSuccess, ENCRYPTED_TYPE, + setupAuthorizeFunc, + setupAuthorizeFind, } from '../test_helpers/repository.test.common'; import { savedObjectsExtensionsMock } from '../mocks/saved_objects_extensions.mock'; @@ -881,6 +882,11 @@ describe('SavedObjectsRepository Spaces Extension', () => { }); describe(`with security extension`, () => { + // Note: resolve, bulkResolve, and collectMultiNamespaceReferences are not tested here because they + // receive parameter arguments from internal methods (internalBulkResolve and the internal + // implementation of collectMultiNamespaceReferences). Arguments to these methods are tested above. + const currentSpace = 'current_space'; + beforeEach(() => { pointInTimeFinderMock.mockClear(); client = elasticsearchClientMock.createElasticsearchClient(); @@ -900,7 +906,7 @@ describe('SavedObjectsRepository Spaces Extension', () => { mockSpacesExt.getSearchableNamespaces.mockImplementation( (namespaces: string[] | undefined): Promise => { if (!namespaces) { - return Promise.resolve([] as string[]); + return Promise.resolve([currentSpace] as string[]); } else if (!namespaces.length) { return Promise.resolve(namespaces); } @@ -915,11 +921,17 @@ describe('SavedObjectsRepository Spaces Extension', () => { } } ); + mockSpacesExt.getCurrentNamespace.mockImplementation((namespace: string | undefined) => { + if (namespace) { + throw SavedObjectsErrorHelpers.createBadRequestError(ERROR_NAMESPACE_SPECIFIED); + } + return currentSpace; + }); }); describe(`#find`, () => { test(`returns empty result if user is unauthorized`, async () => { - setupPerformAuthUnauthorized(mockSecurityExt); + setupAuthorizeFind(mockSecurityExt, 'unauthorized'); const type = 'index-pattern'; const spaceOverride = 'ns-4'; const generatedResults = generateIndexPatternSearchResults(spaceOverride); @@ -927,6 +939,289 @@ describe('SavedObjectsRepository Spaces Extension', () => { const result = await repository.find({ type, namespaces: [spaceOverride] }); expect(result).toEqual(expect.objectContaining({ total: 0 })); }); + + test(`calls authorizeFind with the current namespace`, async () => { + const type = 'index-pattern'; + await findSuccess(client, repository, { type }); + expect(mockSpacesExt.getSearchableNamespaces).toBeCalledTimes(1); + expect(mockSpacesExt.getSearchableNamespaces).toBeCalledWith(undefined); + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeFind).toHaveBeenCalledWith( + expect.objectContaining({ namespaces: new Set([currentSpace]) }) + ); + }); + }); + + describe(`#create`, () => { + test(`calls authorizeCreate with the current namespace`, async () => { + const type = CUSTOM_INDEX_TYPE; + setupAuthorizeFunc(mockSecurityExt.authorizeCreate as jest.Mock, 'fully_authorized'); + await repository.create(type, { attr: 'value' }); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCreate).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#bulkCreate`, () => { + const obj1 = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Test One' }, + references: [{ name: 'ref_0', type: 'test', id: '1' }], + }; + const obj2 = { + type: MULTI_NAMESPACE_TYPE, + id: 'logstash-*', + attributes: { title: 'Test Two' }, + references: [{ name: 'ref_0', type: 'test', id: '2' }], + }; + + beforeEach(() => { + mockPreflightCheckForCreate.mockReset(); + mockPreflightCheckForCreate.mockImplementation(({ objects }) => { + return Promise.resolve(objects.map(({ type, id }) => ({ type, id }))); // respond with no errors by default + }); + }); + + test(`calls authorizeBulkCreate with the current namespace`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkCreate, 'fully_authorized'); + await bulkCreateSuccess(client, repository, [obj1, obj2]); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkCreate).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#get`, () => { + test(`calls authorizeGet with the current namespace`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeGet, 'fully_authorized'); + const type = CUSTOM_INDEX_TYPE; + const id = 'some-id'; + + const response = getMockGetResponse(registry, { + type, + id, + }); + + client.get.mockResponseOnce(response); + await repository.get(type, id); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeGet).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#bulkGet`, () => { + const obj1: SavedObject = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Testing' }, + references: [ + { + name: 'ref_0', + type: 'test', + id: '1', + }, + ], + originId: 'some-origin-id', // only one of the results has an originId, this is intentional to test both a positive and negative case + }; + const obj2: SavedObject = { + type: MULTI_NAMESPACE_TYPE, + id: 'logstash-*', + attributes: { title: 'Testing' }, + references: [ + { + name: 'ref_0', + type: 'test', + id: '2', + }, + ], + }; + + test(`calls authorizeBulkGet with the current namespace`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkGet, 'fully_authorized'); + await bulkGetSuccess(client, repository, registry, [obj1, obj2]); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkGet).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#update`, () => { + test(`calls authorizeUpdate with the current namespace`, async () => { + const type = CUSTOM_INDEX_TYPE; + const id = 'some-id'; + + await updateSuccess( + client, + repository, + registry, + type, + id, + {}, + { upsert: true }, + { mockGetResponseValue: { found: false } as estypes.GetResponse } + ); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdate).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#bulkUpdate`, () => { + const obj1: SavedObjectsBulkUpdateObject = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Test One' }, + }; + const obj2: SavedObjectsBulkUpdateObject = { + type: MULTI_NAMESPACE_TYPE, + id: 'logstash-*', + attributes: { title: 'Test Two' }, + }; + + test(`calls authorizeBulkUpdate with the current namespace`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkUpdate, 'fully_authorized'); + await bulkUpdateSuccess( + client, + repository, + registry, + [obj1, obj2], + undefined, + undefined, + currentSpace + ); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkUpdate).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#delete`, () => { + test(`calls authorizeDelete with the current namespace`, async () => { + const type = CUSTOM_INDEX_TYPE; + const id = 'some-id'; + setupAuthorizeFunc(mockSecurityExt.authorizeBulkDelete, 'fully_authorized'); + await deleteSuccess(client, repository, registry, type, id); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeDelete).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#bulkDelete`, () => { + const obj1: SavedObjectsBulkUpdateObject = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Test One' }, + }; + const obj2: SavedObjectsBulkUpdateObject = { + type: MULTI_NAMESPACE_TYPE, + id: 'logstash-*', + attributes: { title: 'Test Two' }, + }; + + const testObjs = [obj1, obj2]; + const options = { + force: true, + }; + + const internalOptions = { + mockMGetResponseObjects: [ + { + ...obj1, + initialNamespaces: undefined, + }, + { + ...obj2, + initialNamespaces: [currentSpace, 'NS-1', 'NS-2'], + }, + ], + }; + + beforeEach(() => { + mockDeleteLegacyUrlAliases.mockClear(); + mockDeleteLegacyUrlAliases.mockResolvedValue(); + }); + + test(`calls authorizeBulkDelete with the current namespace`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeBulkDelete, 'fully_authorized'); + await bulkDeleteSuccess(client, repository, registry, testObjs, options, internalOptions); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeBulkDelete).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#checkConflicts`, () => { + test(`calls authorizeCheckConflicts with the current namespace`, async () => { + const obj1 = { type: CUSTOM_INDEX_TYPE, id: 'one' }; + const obj2 = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: 'two' }; + + await checkConflictsSuccess(client, repository, registry, [obj1, obj2]); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeCheckConflicts).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#removeReferencesTo`, () => { + test(`calls authorizeRemoveReferences with the current namespace`, async () => { + const type = CUSTOM_INDEX_TYPE; + const id = 'some-id'; + + const query = { query: 1, aggregations: 2 }; + mockGetSearchDsl.mockReturnValue(query); + + await removeReferencesToSuccess(client, repository, type, id); + expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); + expect(mockSpacesExt.getCurrentNamespace).toHaveBeenCalledWith(undefined); + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeRemoveReferences).toHaveBeenCalledWith( + expect.objectContaining({ namespace: currentSpace }) + ); + }); + }); + + describe(`#openPointInTimeForType`, () => { + test(`calls authorizeOpenPointInTime with the current namespace`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeOpenPointInTime, 'fully_authorized'); + await repository.openPointInTimeForType(CUSTOM_INDEX_TYPE); + expect(mockSpacesExt.getSearchableNamespaces).toBeCalledTimes(1); + expect(mockSpacesExt.getSearchableNamespaces).toBeCalledWith(undefined); // will resolve current space + expect(mockSecurityExt.authorizeOpenPointInTime).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeOpenPointInTime).toHaveBeenCalledWith( + expect.objectContaining({ namespaces: new Set([currentSpace]) }) + ); + }); }); }); @@ -972,7 +1267,7 @@ describe('SavedObjectsRepository Spaces Extension', () => { }); describe(`#create`, () => { - test(`calls encryptAttributes with the current namespace by default`, async () => { + test(`calls encryptAttributes with the current namespace`, async () => { mockEncryptionExt.isEncryptableType.mockReturnValue(true); await repository.create(encryptedSO.type, encryptedSO.attributes); expect(mockSpacesExt.getCurrentNamespace).toBeCalledTimes(1); @@ -1002,7 +1297,7 @@ describe('SavedObjectsRepository Spaces Extension', () => { references: [{ name: 'ref_0', type: 'test', id: '1' }], }; - test(`calls encryptAttributes with the current namespace by default`, async () => { + test(`calls encryptAttributes with the current namespace`, async () => { mockEncryptionExt.isEncryptableType.mockReturnValueOnce(false); mockEncryptionExt.isEncryptableType.mockReturnValueOnce(true); mockEncryptionExt.isEncryptableType.mockReturnValueOnce(false); @@ -1038,7 +1333,7 @@ describe('SavedObjectsRepository Spaces Extension', () => { }); describe(`#update`, () => { - it('calls encryptAttributes with the current namespace by default', async () => { + it('calls encryptAttributes with the current namespace', async () => { mockEncryptionExt.isEncryptableType.mockReturnValue(true); mockEncryptionExt.decryptOrStripResponseAttributes.mockResolvedValue({ ...encryptedSO, @@ -1085,7 +1380,7 @@ describe('SavedObjectsRepository Spaces Extension', () => { attributes: { title: 'Test Two' }, }; - it(`calls encryptAttributes with the current namespace by default`, async () => { + it(`calls encryptAttributes with the current namespace`, async () => { mockEncryptionExt.isEncryptableType.mockReturnValueOnce(false); mockEncryptionExt.isEncryptableType.mockReturnValueOnce(true); mockEncryptionExt.isEncryptableType.mockReturnValueOnce(false); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts index 1a0fbb2805daf..0b395b6154819 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts @@ -23,7 +23,6 @@ import { import type { Payload } from '@hapi/boom'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { SavedObject, SavedObjectReference } from '@kbn/core-saved-objects-server'; import type { SavedObjectsBaseOptions, SavedObjectsFindOptions, @@ -52,11 +51,12 @@ import type { SavedObjectsRawDoc, SavedObjectsRawDocSource, SavedObjectUnsanitizedDoc, + SavedObject, + SavedObjectReference, + BulkResolveError, } from '@kbn/core-saved-objects-server'; -import { - SavedObjectsErrorHelpers, - ALL_NAMESPACES_STRING, -} from '@kbn/core-saved-objects-utils-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { SavedObjectsRepository } from './repository'; import { PointInTimeFinder } from './point_in_time_finder'; import { loggerMock } from '@kbn/logging-mocks'; @@ -69,7 +69,6 @@ import { kibanaMigratorMock } from '../mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import * as esKuery from '@kbn/es-query'; import { errors as EsErrors } from '@elastic/elasticsearch'; -import type { InternalBulkResolveError } from './internal_bulk_resolve'; import { CUSTOM_INDEX_TYPE, @@ -4069,7 +4068,7 @@ describe('SavedObjectsRepository', () => { it('throws when internalBulkResolve result is an error', async () => { const error = SavedObjectsErrorHelpers.decorateBadRequestError(new Error('Oh no!')); - const expectedResult: InternalBulkResolveError = { type: 'obj-type', id: 'obj-id', error }; + const expectedResult: BulkResolveError = { type: 'obj-type', id: 'obj-id', error }; mockInternalBulkResolve.mockResolvedValue({ resolved_objects: [expectedResult] }); await expect(repository.resolve('foo', '2')).rejects.toEqual(error); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts index 1c36ad1edc8b8..2e2525d3d3b8b 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.ts @@ -17,7 +17,7 @@ import { isSupportedEsServer, isNotFoundFromUnsupportedServer, } from '@kbn/core-elasticsearch-server-internal'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; +import type { BulkResolveError } from '@kbn/core-saved-objects-server'; import type { SavedObjectsBaseOptions, SavedObjectsIncrementCounterOptions, @@ -69,15 +69,15 @@ import { type ISavedObjectsEncryptionExtension, type ISavedObjectsSecurityExtension, type ISavedObjectsSpacesExtension, - AuditAction, type CheckAuthorizationResult, type AuthorizationTypeMap, + AuthorizeCreateObject, + AuthorizeUpdateObject, + type AuthorizeBulkGetObject, + type SavedObject, } from '@kbn/core-saved-objects-server'; -import { - DEFAULT_NAMESPACE_STRING, - SavedObjectsErrorHelpers, - type DecoratedError, -} from '@kbn/core-saved-objects-utils-server'; +import { DEFAULT_NAMESPACE_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers, type DecoratedError } from '@kbn/core-saved-objects-server'; import { ALL_NAMESPACES_STRING, FIND_DEFAULT_PAGE, @@ -101,11 +101,7 @@ import { PointInTimeFinder } from './point_in_time_finder'; import { createRepositoryEsClient, type RepositoryEsClient } from './repository_es_client'; import { getSearchDsl } from './search_dsl'; import { includedFields } from './included_fields'; -import { - internalBulkResolve, - type InternalBulkResolveError, - isBulkResolveError, -} from './internal_bulk_resolve'; +import { internalBulkResolve, isBulkResolveError } from './internal_bulk_resolve'; import { validateConvertFilterToKueryNode } from './filter_utils'; import { validateAndConvertAggregations } from './aggregations'; import { @@ -351,28 +347,14 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { existingOriginId = preflightResult?.existingDocument?._source?.originId; } - const spacesToEnforce = new Set(initialNamespaces).add(namespaceString); // Always check/enforce authZ for the active space - const existingNamespaces = preflightResult?.existingDocument?._source?.namespaces || []; - const spacesToAuthorize = new Set(existingNamespaces); - spacesToAuthorize.delete(ALL_NAMESPACES_STRING); // Don't accidentally check for global privileges when the object exists in '*' - const authorizationResult = await this._securityExtension?.performAuthorization({ - // If a user tries to create an object with `initialNamespaces: ['*']`, they need to have 'create' privileges for the Global Resource - // (e.g., All privileges for All Spaces). - // Inversely, if a user tries to overwrite an object that already exists in '*', they don't need to 'create' privileges for the Global - // Resource, so in that case we have to filter out that string from spacesToAuthorize (because `allowGlobalResource: true` is used - // below.) - actions: new Set(['create']), - types: new Set([type]), - spaces: new Set([...spacesToEnforce, ...spacesToAuthorize]), // existing namespaces are included so we can later redact if necessary - enforceMap: new Map([[type, spacesToEnforce]]), - auditCallback: (error) => - this._securityExtension!.addAuditEvent({ - action: AuditAction.CREATE, - savedObject: { type, id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the create operation has not occurred yet - }), - options: { allowGlobalResource: true }, + const authorizationResult = await this._securityExtension?.authorizeCreate({ + namespace, + object: { + type, + id, + initialNamespaces, + existingNamespaces: preflightResult?.existingDocument?._source?.namespaces ?? [], + }, }); if (preflightResult?.error) { @@ -524,45 +506,20 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { objects: preflightCheckObjects, }); - const typesAndSpaces = new Map>(); - const spacesToAuthorize = new Set([namespaceString]); // Always check authZ for the active space - for (const { value } of validObjects) { - const { object, preflightCheckIndex: index } = value; + const authObjects: AuthorizeCreateObject[] = validObjects.map((element) => { + const { object, preflightCheckIndex: index } = element.value; const preflightResult = index !== undefined ? preflightCheckResponse[index] : undefined; + return { + type: object.type, + id: object.id, + initialNamespaces: object.initialNamespaces, + existingNamespaces: preflightResult?.existingDocument?._source.namespaces ?? [], + }; + }); - const spacesToEnforce = typesAndSpaces.get(object.type) ?? new Set([namespaceString]); // Always enforce authZ for the active space - for (const space of object.initialNamespaces ?? []) { - spacesToEnforce.add(space); - spacesToAuthorize.add(space); - } - typesAndSpaces.set(object.type, spacesToEnforce); - for (const space of preflightResult?.existingDocument?._source.namespaces ?? []) { - if (space === ALL_NAMESPACES_STRING) continue; // Don't accidentally check for global privileges when the object exists in '*' - spacesToAuthorize.add(space); // existing namespaces are included so we can later redact if necessary - } - } - - const authorizationResult = await this._securityExtension?.performAuthorization({ - // If a user tries to create an object with `initialNamespaces: ['*']`, they need to have 'bulk_create' privileges for the Global - // Resource (e.g., All privileges for All Spaces). - // Inversely, if a user tries to overwrite an object that already exists in '*', they don't need to have 'bulk_create' privileges for the Global - // Resource, so in that case we have to filter out that string from spacesToAuthorize (because `allowGlobalResource: true` is used - // below.) - actions: new Set(['bulk_create']), - types: new Set(typesAndSpaces.keys()), - spaces: spacesToAuthorize, - enforceMap: typesAndSpaces, - auditCallback: (error) => { - for (const { value } of validObjects) { - this._securityExtension!.addAuditEvent({ - action: AuditAction.CREATE, - savedObject: { type: value.object.type, id: value.object.id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the create operation has not occurred yet - }); - } - }, - options: { allowGlobalResource: true }, + const authorizationResult = await this._securityExtension?.authorizeBulkCreate({ + namespace, + objects: authObjects, }); let bulkRequestIndexCounter = 0; @@ -759,21 +716,10 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { }; }); - const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); const validObjects = expectedBulkGetResults.filter(isRight); - const typesAndSpaces = new Map>(); - for (const { value } of validObjects) { - typesAndSpaces.set(value.type, new Set([namespaceString])); // Always enforce authZ for the active space - } - - await this._securityExtension?.performAuthorization({ - actions: new Set(['bulk_create']), - types: new Set(typesAndSpaces.keys()), - spaces: new Set([namespaceString]), // Always check authZ for the active space - enforceMap: typesAndSpaces, - // auditCallback is intentionally omitted, this function in the previous Security SOC wrapper implementation - // did not have audit logging. This is primarily because it is only used by Kibana and is not exposed in a - // public HTTP API + await this._securityExtension?.authorizeCheckConflicts({ + namespace, + objects: validObjects.map((element) => ({ type: element.value.type, id: element.value.id })), }); const bulkGetDocs = validObjects.map(({ value: { type, id } }) => ({ @@ -841,22 +787,11 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { const { refresh = DEFAULT_REFRESH_SETTING, force } = options; - const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); - const typesAndSpaces = new Map>([[type, new Set([namespaceString])]]); // Always enforce authZ for the active space - - await this._securityExtension?.performAuthorization({ - actions: new Set(['delete']), - types: new Set([type]), - spaces: new Set([namespaceString]), // Always check authZ for the active space - enforceMap: typesAndSpaces, - auditCallback: (error) => { - this._securityExtension!.addAuditEvent({ - action: AuditAction.DELETE, - savedObject: { type, id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the delete operation has not occurred yet - }); - }, + // we don't need to pass existing namespaces in because we're only concerned with authorizing + // the current space. This saves us from performing the preflight check if we're unauthorized + await this._securityExtension?.authorizeDelete({ + namespace, + object: { type, id }, }); const rawId = this._serializer.generateRawId(namespace, type, id); @@ -1123,42 +1058,25 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { force, }); - // Perform Auth Check (on both L/R, we'll deal with that later) - const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); - const typesAndSpaces = new Map>(); - const spacesToAuthorize = new Set([namespaceString]); // Always check authZ for the active space if (this._securityExtension) { - for (const { value } of expectedBulkDeleteMultiNamespaceDocsResults) { - const index = (value as { esRequestIndex: number }).esRequestIndex; - const { type } = value; - const preflightResult = - index !== undefined ? multiNamespaceDocsResponse?.body.docs[index] : undefined; - - const spacesToEnforce = typesAndSpaces.get(type) ?? new Set([namespaceString]); // Always enforce authZ for the active space - typesAndSpaces.set(type, spacesToEnforce); - // @ts-expect-error MultiGetHit._source is optional - for (const space of preflightResult?._source?.namespaces ?? []) { - spacesToAuthorize.add(space); // existing namespaces are included - } - } - } + // Perform Auth Check (on both L/R, we'll deal with that later) + const authObjects: AuthorizeUpdateObject[] = expectedBulkDeleteMultiNamespaceDocsResults.map( + (element) => { + const index = (element.value as { esRequestIndex: number }).esRequestIndex; + const { type, id } = element.value; + const preflightResult = + index !== undefined ? multiNamespaceDocsResponse?.body.docs[index] : undefined; - await this._securityExtension?.performAuthorization({ - actions: new Set(['bulk_delete']), - types: new Set(typesAndSpaces.keys()), - spaces: spacesToAuthorize, - enforceMap: typesAndSpaces, - auditCallback: (error) => { - for (const { value } of expectedBulkDeleteMultiNamespaceDocsResults) { - this._securityExtension!.addAuditEvent({ - action: AuditAction.DELETE, - savedObject: { type: value.type, id: value.id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the delete operation has not occurred yet - }); + return { + type, + id, + // @ts-expect-error MultiGetHit._source is optional + existingNamespaces: preflightResult?._source?.namespaces ?? [], + }; } - }, - }); + ); + await this._securityExtension.authorizeBulkDelete({ namespace, objects: authObjects }); + } // Filter valid objects const validObjects = expectedBulkDeleteMultiNamespaceDocsResults.filter(isRight); @@ -1438,30 +1356,23 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { } } - // We have to first do a "pre-authorization" check so that we can construct the search DSL accordingly - const spacesToPreauthorize = new Set(namespaces); + // We have to first perform an initial authorization check so that we can construct the search DSL accordingly + const spacesToAuthorize = new Set(namespaces); + const typesToAuthorize = new Set(types); let typeToNamespacesMap: Map | undefined; - let preAuthorizationResult: CheckAuthorizationResult<'find'> | undefined; + let authorizationResult: CheckAuthorizationResult | undefined; if (!disableExtensions && this._securityExtension) { - preAuthorizationResult = await this._securityExtension.performAuthorization({ - actions: new Set(['find']), - types: new Set(types), - spaces: spacesToPreauthorize, + authorizationResult = await this._securityExtension.authorizeFind({ + namespaces: spacesToAuthorize, + types: typesToAuthorize, }); - if (preAuthorizationResult.status === 'unauthorized') { + if (authorizationResult?.status === 'unauthorized') { // If the user is unauthorized to find *anything* they requested, return an empty response - this._securityExtension.addAuditEvent({ - action: AuditAction.FIND, - error: new Error(`User is unauthorized for any requested types/spaces.`), - // TODO: include object type(s) that were requested? - // requestedTypes: types, - // requestedSpaces: namespaces, - }); return SavedObjectsUtils.createEmptyFindResponse(options); } - if (preAuthorizationResult.status === 'partially_authorized') { + if (authorizationResult?.status === 'partially_authorized') { typeToNamespacesMap = new Map(); - for (const [objType, entry] of preAuthorizationResult.typeMap) { + for (const [objType, entry] of authorizationResult.typeMap) { if (!entry.find) continue; // This ensures that the query DSL can filter only for object types that the user is authorized to access for a given space const { authorizedSpaces, isGloballyAuthorized } = entry.find; @@ -1542,30 +1453,22 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { return result; } - const spacesToAuthorize = new Set(spacesToPreauthorize); // only for namespace redaction - for (const { type: objType, id, namespaces: objectNamespaces = [] } of result.saved_objects) { - for (const space of objectNamespaces) { - spacesToAuthorize.add(space); - } - this._securityExtension?.addAuditEvent({ - action: AuditAction.FIND, - savedObject: { type: objType, id }, - }); - } - const authorizationResult = - spacesToAuthorize.size > spacesToPreauthorize.size - ? // If there are any namespaces in the object results that were not already checked during pre-authorization, we need *another* - // authorization check so we can correctly redact the object namespaces below. - await this._securityExtension?.performAuthorization({ - actions: new Set(['find']), - types: new Set(types), - spaces: spacesToAuthorize, - }) - : undefined; + // Now that we have a full set of results with all existing namespaces for each object, + // we need an updated authorization type map to pass on to the redact method + const redactTypeMap = await this._securityExtension?.getFindRedactTypeMap({ + previouslyCheckedNamespaces: spacesToAuthorize, + objects: result.saved_objects.map((obj) => { + return { + type: obj.type, + id: obj.id, + existingNamespaces: obj.namespaces ?? [], + }; + }), + }); return this.optionallyDecryptAndRedactBulkResult( result, - authorizationResult?.typeMap ?? preAuthorizationResult?.typeMap // If we made a second authorization check, use that one; otherwise, fall back to the pre-authorization check + redactTypeMap ?? authorizationResult?.typeMap // If the redact type map is valid, use that one; otherwise, fall back to the authorization check ); } @@ -1682,37 +1585,38 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(); } - const typesAndSpaces = new Map>(); - const spacesToAuthorize = new Set([SavedObjectsUtils.namespaceIdToString(namespace)]); // Always check authZ for the active space + const authObjects: AuthorizeBulkGetObject[] = []; const result = { saved_objects: expectedBulkGetResults.map((expectedResult) => { if (isLeft(expectedResult)) { + const { type, id } = expectedResult.value; + authObjects.push({ type, id, existingNamespaces: [], error: true }); return expectedResult.value as any; } const { type, id, - namespaces = [SavedObjectsUtils.namespaceIdToString(namespace)], // set to default value for `rawDocExistsInNamespaces` check below + // set to default namespaces value for `rawDocExistsInNamespaces` check below + namespaces = [SavedObjectsUtils.namespaceIdToString(namespace)], esRequestIndex, } = expectedResult.value; - const doc = bulkGetResponse?.body.docs[esRequestIndex]; - const spacesToEnforce = - typesAndSpaces.get(type) ?? new Set([SavedObjectsUtils.namespaceIdToString(namespace)]); // Always enforce authZ for the active space - for (const space of namespaces) { - spacesToEnforce.add(space); - typesAndSpaces.set(type, spacesToEnforce); - spacesToAuthorize.add(space); - } + const doc = bulkGetResponse?.body.docs[esRequestIndex]; // @ts-expect-error MultiGetHit._source is optional - for (const space of doc?._source?.namespaces ?? []) { - spacesToAuthorize.add(space); // existing namespaces are included so we can later redact if necessary - } + const docNotFound = !doc?.found || !this.rawDocExistsInNamespaces(doc, namespaces); - // @ts-expect-error MultiGetHit._source is optional - if (!doc?.found || !this.rawDocExistsInNamespaces(doc, namespaces)) { + authObjects.push({ + type, + id, + objectNamespaces: namespaces, + // @ts-expect-error MultiGetHit._source is optional + existingNamespaces: doc?._source?.namespaces ?? [], + error: docNotFound, + }); + + if (docNotFound) { return { id, type, @@ -1725,21 +1629,9 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { }), }; - const authorizationResult = await this._securityExtension?.performAuthorization({ - actions: new Set(['bulk_get']), - types: new Set(typesAndSpaces.keys()), - spaces: spacesToAuthorize, - enforceMap: typesAndSpaces, - auditCallback: (error) => { - for (const { type, id, error: bulkError } of result.saved_objects) { - if (!error && !!bulkError) continue; // Only log success events for objects that were actually found (and are being returned to the user) - this._securityExtension!.addAuditEvent({ - action: AuditAction.GET, - savedObject: { type, id }, - error, - }); - } - }, + const authorizationResult = await this._securityExtension?.authorizeBulkGet({ + namespace, + objects: authObjects, }); return this.optionallyDecryptAndRedactBulkResult(result, authorizationResult?.typeMap); @@ -1768,7 +1660,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { const resolvedObjects = bulkResults.map>((result) => { // extract payloads from saved object errors if (isBulkResolveError(result)) { - const errorResult = result as InternalBulkResolveError; + const errorResult = result as BulkResolveError; const { type, id, error } = errorResult; return { saved_object: { type, id, error: errorContent(error) } as unknown as SavedObject, @@ -1806,39 +1698,23 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(type, id); } - const spacesToEnforce = new Set([SavedObjectsUtils.namespaceIdToString(namespace)]); // Always check/enforce authZ for the active space - const existingNamespaces = body?._source?.namespaces || []; + const objectNotFound = + !isFoundGetResponse(body) || indexNotFound || !this.rawDocExistsInNamespace(body, namespace); - const authorizationResult = await this._securityExtension?.performAuthorization({ - actions: new Set(['get']), - types: new Set([type]), - spaces: new Set([...spacesToEnforce, ...existingNamespaces]), // existing namespaces are included so we can later redact if necessary - enforceMap: new Map([[type, spacesToEnforce]]), - auditCallback: (error) => { - if (error) { - this._securityExtension!.addAuditEvent({ - action: AuditAction.GET, - savedObject: { type, id }, - error, - }); - } - // Audit event for success case is added separately below + const authorizationResult = await this._securityExtension?.authorizeGet({ + namespace, + object: { + type, + id, + existingNamespaces: body?._source?.namespaces ?? [], }, + objectNotFound, }); - if ( - !isFoundGetResponse(body) || - indexNotFound || - !this.rawDocExistsInNamespace(body, namespace) - ) { + if (objectNotFound) { // see "404s from missing index" above throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - // Only log a success event if the object was actually found (and is being returned to the user) - this._securityExtension?.addAuditEvent({ - action: AuditAction.GET, - savedObject: { type, id }, - }); const result = getSavedObjectFromSource(this._registry, type, id, body); @@ -1908,21 +1784,11 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { }); } - const spacesToEnforce = new Set([SavedObjectsUtils.namespaceIdToString(namespace)]); // Always check/enforce authZ for the active space - const existingNamespaces = preflightResult?.savedObjectNamespaces || []; - - const authorizationResult = await this._securityExtension?.performAuthorization({ - actions: new Set(['update']), - types: new Set([type]), - spaces: new Set([...spacesToEnforce, ...existingNamespaces]), // existing namespaces are included so we can later redact if necessary - enforceMap: new Map([[type, spacesToEnforce]]), - auditCallback: (error) => - this._securityExtension!.addAuditEvent({ - action: AuditAction.UPDATE, - savedObject: { type, id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the update/upsert operation has not occurred yet - }), + const existingNamespaces = preflightResult?.savedObjectNamespaces ?? []; + + const authorizationResult = await this._securityExtension?.authorizeUpdate({ + namespace, + object: { type, id, existingNamespaces }, }); if ( @@ -2106,9 +1972,6 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { } } - // `objectNamespace` is a namespace string, while `namespace` is a namespace ID. - // The object namespace string, if defined, will supersede the operation's namespace ID. - if (error) { return { tag: 'Left', @@ -2148,6 +2011,8 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { }; } + // `objectNamespace` is a namespace string, while `namespace` is a namespace ID. + // The object namespace string, if defined, will supersede the operation's namespace ID. const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); const getNamespaceId = (objectNamespace?: string) => objectNamespace !== undefined @@ -2175,38 +2040,21 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(); } - const typesAndSpaces = new Map>(); - const spacesToAuthorize = new Set([namespaceString]); // Always check authZ for the active space - for (const { value } of validObjects) { - const { type, objectNamespace, esRequestIndex: index } = value; - const objectNamespaceString = getNamespaceString(objectNamespace); + const authObjects: AuthorizeUpdateObject[] = validObjects.map((element) => { + const { type, id, objectNamespace, esRequestIndex: index } = element.value; const preflightResult = index !== undefined ? bulkGetResponse?.body.docs[index] : undefined; + return { + type, + id, + objectNamespace, + // @ts-expect-error MultiGetHit._source is optional + existingNamespaces: preflightResult?._source?.namespaces ?? [], + }; + }); - const spacesToEnforce = typesAndSpaces.get(type) ?? new Set([namespaceString]); // Always enforce authZ for the active space - spacesToEnforce.add(objectNamespaceString); - typesAndSpaces.set(type, spacesToEnforce); - spacesToAuthorize.add(objectNamespaceString); - // @ts-expect-error MultiGetHit._source is optional - for (const space of preflightResult?._source?.namespaces ?? []) { - spacesToAuthorize.add(space); // existing namespaces are included so we can later redact if necessary - } - } - - const authorizationResult = await this._securityExtension?.performAuthorization({ - actions: new Set(['bulk_update']), - types: new Set(typesAndSpaces.keys()), - spaces: spacesToAuthorize, - enforceMap: typesAndSpaces, - auditCallback: (error) => { - for (const { value } of validObjects) { - this._securityExtension!.addAuditEvent({ - action: AuditAction.UPDATE, - savedObject: { type: value.type, id: value.id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the update operation has not occurred yet - }); - } - }, + const authorizationResult = await this._securityExtension?.authorizeBulkUpdate({ + namespace, + objects: authObjects, }); let bulkUpdateRequestIndexCounter = 0; @@ -2363,22 +2211,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { const namespace = this.getCurrentNamespace(options.namespace); const { refresh = true } = options; - // TODO: Improve authorization and auditing (https://github.com/elastic/kibana/issues/135259) - - const spaces = new Set([SavedObjectsUtils.namespaceIdToString(namespace)]); // Always check/enforce authZ for the active space - await this._securityExtension?.performAuthorization({ - actions: new Set(['delete']), - types: new Set([type]), - spaces, - enforceMap: new Map([[type, spaces]]), - auditCallback: (error) => - this._securityExtension!.addAuditEvent({ - action: AuditAction.REMOVE_REFERENCES, - savedObject: { type, id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the updateByQuery operation has not occurred yet - }), - }); + await this._securityExtension?.authorizeRemoveReferences({ namespace, object: { type, id } }); const allTypes = this._registry.getAllTypes().map((t) => t.name); @@ -2650,29 +2483,9 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { } if (!disableExtensions && this._securityExtension) { - const spaces = new Set(namespaces); - const preAuthorizationResult = await this._securityExtension?.performAuthorization({ - actions: new Set(['open_point_in_time']), + await this._securityExtension.authorizeOpenPointInTime({ + namespaces: new Set(namespaces), types: new Set(types), - spaces, - }); - if (preAuthorizationResult.status === 'unauthorized') { - // If the user is unauthorized to find *anything* they requested, return an empty response - this._securityExtension.addAuditEvent({ - action: AuditAction.OPEN_POINT_IN_TIME, - error: new Error('User is unauthorized for any requested types/spaces.'), - // TODO: include object type(s) that were requested? - // requestedTypes: types, - // requestedSpaces: namespaces, - }); - throw SavedObjectsErrorHelpers.decorateForbiddenError(new Error('unauthorized')); - } - this._securityExtension.addAuditEvent({ - action: AuditAction.OPEN_POINT_IN_TIME, - outcome: 'unknown', - // TODO: include object type(s) that were requested? - // requestedTypes: types, - // requestedSpaces: namespaces, }); } @@ -2711,10 +2524,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository { const { disableExtensions } = internalOptions; if (!disableExtensions && this._securityExtension) { - this._securityExtension.addAuditEvent({ - action: AuditAction.CLOSE_POINT_IN_TIME, - outcome: 'unknown', - }); + this._securityExtension.auditClosePointInTime(); } return await this.client.closePointInTime({ diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository_es_client.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository_es_client.test.ts index cbd8f81bfc0f5..a0afafa2582ff 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository_es_client.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository_es_client.test.ts @@ -10,7 +10,7 @@ import { retryCallClusterMock } from './repository_es_client.test.mock'; import { createRepositoryEsClient, RepositoryEsClient } from './repository_es_client'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; describe('RepositoryEsClient', () => { let client: ReturnType; diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.test.ts index 6016ef84fdc40..7432b7ae3e6ae 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.test.ts @@ -17,23 +17,21 @@ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-m import { loggerMock } from '@kbn/logging-mocks'; import type { SavedObjectsUpdateObjectsSpacesObject } from '@kbn/core-saved-objects-api-server'; -import { - SavedObjectsErrorHelpers, - ALL_NAMESPACES_STRING, - setsAreEqual, - setMapsAreEqual, -} from '@kbn/core-saved-objects-utils-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import type { UpdateObjectsSpacesParams } from './update_objects_spaces'; import { updateObjectsSpaces } from './update_objects_spaces'; -import { AuditAction, type ISavedObjectsSecurityExtension } from '@kbn/core-saved-objects-server'; +import { + ISavedObjectsSecurityExtension, + SavedObjectsErrorHelpers, +} from '@kbn/core-saved-objects-server'; import { checkAuthError, enforceError, - setupPerformAuthFullyAuthorized, - setupPerformAuthEnforceFailure, setupRedactPassthrough, + authMap, + setupAuthorizeFunc, } from '../test_helpers/repository.test.common'; import { savedObjectsExtensionsMock } from '../mocks/saved_objects_extensions.mock'; @@ -651,246 +649,213 @@ describe('#updateObjectsSpaces', () => { let mockSecurityExt: jest.Mocked; let params: UpdateObjectsSpacesParams; + const otherSpace = 'space-to-add'; + const obj1 = { + type: SHAREABLE_OBJ_TYPE, + id: 'id-1', + spaces: [ALL_NAMESPACES_STRING, otherSpace], + }; + const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2', spaces: [ALL_NAMESPACES_STRING] }; + const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3', spaces: [EXISTING_SPACE, otherSpace] }; + const obj4 = { type: SHAREABLE_OBJ_TYPE, id: 'id-4', spaces: [EXISTING_SPACE] }; + + const objects = [obj1, obj2, obj3, obj4]; + const spacesToAdd = [otherSpace]; + const spacesToRemove = [EXISTING_SPACE]; + afterEach(() => { - mockSecurityExt.performAuthorization.mockClear(); mockSecurityExt.redactNamespaces.mockClear(); }); - describe(`errors`, () => { - beforeEach(() => { - const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1' }; - const objects = [obj1]; - const spacesToAdd = ['foo-space']; - mockSecurityExt = savedObjectsExtensionsMock.createSecurityExtension(); - params = setup({ objects, spacesToAdd }, mockSecurityExt); - mockMgetResults({ found: true, namespaces: [EXISTING_SPACE] }); // result for obj1 - mockBulkResults({ error: false }); // result for obj1 - }); - - test(`propagates error from es client bulk get`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); + beforeEach(() => { + mockSecurityExt = savedObjectsExtensionsMock.createSecurityExtension(); + params = setup( + { objects, spacesToAdd, spacesToRemove, options: { namespace: 'foo-namespace' } }, + mockSecurityExt + ); + mockMgetResults( + { found: true, namespaces: [ALL_NAMESPACES_STRING, otherSpace] }, // result for obj1 -- will not be changed + { found: true, namespaces: [ALL_NAMESPACES_STRING] }, // result for obj2 -- will be updated to add otherSpace + { found: true, namespaces: [EXISTING_SPACE, otherSpace] }, // result for obj3 -- will be updated to remove EXISTING_SPACE + { found: true, namespaces: [EXISTING_SPACE] } // result for obj4 -- will be updated to remove EXISTING_SPACE and add otherSpace + ); + mockBulkResults({ error: false }, { error: false }, { error: false }); // results for obj2, obj3, and obj4 + }); - const error = SavedObjectsErrorHelpers.createBadRequestError('OOPS!'); + test(`propagates error from es client bulk get`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdateSpaces, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); - mockGetBulkOperationError.mockReset(); - client.bulk.mockReset(); - client.bulk.mockImplementationOnce(() => { - throw error; - }); + const error = SavedObjectsErrorHelpers.createBadRequestError('OOPS!'); - await expect(updateObjectsSpaces(params)).rejects.toThrow(error); + mockGetBulkOperationError.mockReset(); + client.bulk.mockReset(); + client.bulk.mockImplementationOnce(() => { + throw error; }); - test(`propagates decorated error when performAuthorization rejects promise`, async () => { - mockSecurityExt.performAuthorization.mockRejectedValueOnce(checkAuthError); + await expect(updateObjectsSpaces(params)).rejects.toThrow(error); + }); - await expect(updateObjectsSpaces(params)).rejects.toThrow(checkAuthError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - }); + test(`propagates decorated error when authorizeUpdateSpaces rejects promise`, async () => { + mockSecurityExt.authorizeUpdateSpaces.mockRejectedValueOnce(checkAuthError); - test(`propagates decorated error when unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + await expect(updateObjectsSpaces(params)).rejects.toThrow(checkAuthError); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledTimes(1); + }); - await expect(updateObjectsSpaces(params)).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - }); + test(`propagates decorated error when unauthorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdateSpaces, 'unauthorized'); - test(`adds audit event when not unauthorized`, async () => { - setupPerformAuthEnforceFailure(mockSecurityExt); + await expect(updateObjectsSpaces(params)).rejects.toThrow(enforceError); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledTimes(1); + }); - await expect(updateObjectsSpaces(params)).rejects.toThrow(enforceError); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); + test(`returns result when authorized`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdateSpaces, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE_OBJECTS_SPACES, - addToSpaces: params.spacesToAdd, - deleteFromSpaces: undefined, - savedObject: { type: params.objects[0].type, id: params.objects[0].id }, - error: enforceError, - }); - }); + const result = await updateObjectsSpaces(params); - test(`returns error from es client bulk operation`, async () => { - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - - mockGetBulkOperationError.mockReset(); - client.bulk.mockReset(); - mockBulkResults({ error: true }); - - const result = await updateObjectsSpaces(params); - expect(result).toEqual({ - objects: [ - { - error: BULK_ERROR, - id: params.objects[0].id, - spaces: [], - type: params.objects[0].type, - }, - ], - }); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledTimes(1); + expect(client.bulk).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + objects: [ + obj1, + { ...obj2, spaces: [...obj2.spaces, otherSpace] }, + { ...obj3, spaces: spacesToAdd }, + { ...obj4, spaces: spacesToAdd }, + ], }); }); - describe('success', () => { - const defaultSpace = 'default'; - const otherSpace = 'space-to-add'; - const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1' }; - const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2' }; - const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3' }; - const obj4 = { type: SHAREABLE_OBJ_TYPE, id: 'id-4' }; + test(`calls authorizeUpdateSpaces with correct parameters`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdateSpaces, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); - const objects = [obj1, obj2, obj3, obj4]; - const spacesToAdd = [otherSpace]; - const spacesToRemove = [EXISTING_SPACE]; + await updateObjectsSpaces(params); - beforeEach(() => { - mockSecurityExt = savedObjectsExtensionsMock.createSecurityExtension(); - params = setup({ objects, spacesToAdd, spacesToRemove }, mockSecurityExt); - mockMgetResults( - { found: true, namespaces: [ALL_NAMESPACES_STRING, otherSpace] }, // result for obj1 -- will not be changed - { found: true, namespaces: [ALL_NAMESPACES_STRING] }, // result for obj2 -- will be updated to add otherSpace - { found: true, namespaces: [EXISTING_SPACE, otherSpace] }, // result for obj3 -- will be updated to remove EXISTING_SPACE - { found: true, namespaces: [EXISTING_SPACE] } // result for obj4 -- will be updated to remove EXISTING_SPACE and add otherSpace - ); - mockBulkResults({ error: false }, { error: false }, { error: false }); // results for obj2, obj3, and obj4 - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledWith({ + namespace: 'foo-namespace', + spacesToAdd, + spacesToRemove, + objects: [ + { + type: obj1.type, + id: obj1.id, + existingNamespaces: obj1.spaces, + }, + { + type: obj2.type, + id: obj2.id, + existingNamespaces: obj2.spaces, + }, + { + type: obj3.type, + id: obj3.id, + existingNamespaces: obj3.spaces, + }, + { + type: obj4.type, + id: obj4.id, + existingNamespaces: obj4.spaces, + }, + ], }); + }); - test(`calls performAuthorization with correct actions, types, spaces, and enforce map`, async () => { - await updateObjectsSpaces(params); - - expect(client.bulk).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['share_to_space']); - const expectedSpaces = new Set([defaultSpace, otherSpace, EXISTING_SPACE]); - const expectedTypes = new Set([SHAREABLE_OBJ_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set( - SHAREABLE_OBJ_TYPE, - new Set([defaultSpace, otherSpace, EXISTING_SPACE]) - ); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); + test(`calls authorizeUpdateSpaces with '*' when spacesToAdd includes '*'`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdateSpaces, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); + + await updateObjectsSpaces({ ...params, spacesToAdd: ['*'] }); + + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledWith({ + namespace: 'foo-namespace', + spacesToAdd: ['*'], + spacesToRemove, + objects: [ + { + type: obj1.type, + id: obj1.id, + existingNamespaces: obj1.spaces, + }, + { + type: obj2.type, + id: obj2.id, + existingNamespaces: obj2.spaces, + }, + { + type: obj3.type, + id: obj3.id, + existingNamespaces: obj3.spaces, + }, + { + type: obj4.type, + id: obj4.id, + existingNamespaces: obj4.spaces, + }, + ], }); + }); - test(`adds audit event per object when successful`, async () => { - await updateObjectsSpaces(params); - - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledTimes(objects.length); - objects.forEach((obj) => { - expect(mockSecurityExt.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE_OBJECTS_SPACES, - savedObject: { type: obj.type, id: obj.id }, - outcome: 'unknown', - addToSpaces: spacesToAdd, - deleteFromSpaces: spacesToRemove, - error: undefined, - }); - }); + test(`calls authorizeUpdateSpaces with '*' when spacesToRemove includes '*'`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdateSpaces, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); + + await updateObjectsSpaces({ ...params, spacesToRemove: ['*'] }); + + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledWith({ + namespace: 'foo-namespace', + spacesToAdd, + spacesToRemove: ['*'], + objects: [ + { + type: obj1.type, + id: obj1.id, + existingNamespaces: obj1.spaces, + }, + { + type: obj2.type, + id: obj2.id, + existingNamespaces: obj2.spaces, + }, + { + type: obj3.type, + id: obj3.id, + existingNamespaces: obj3.spaces, + }, + { + type: obj4.type, + id: obj4.id, + existingNamespaces: obj4.spaces, + }, + ], }); }); - describe('all spaces', () => { - const defaultSpace = 'default'; - const otherSpace = 'space-to-add'; - const obj1 = { type: SHAREABLE_OBJ_TYPE, id: 'id-1' }; - const obj2 = { type: SHAREABLE_OBJ_TYPE, id: 'id-2' }; - const obj3 = { type: SHAREABLE_OBJ_TYPE, id: 'id-3' }; - const obj4 = { type: SHAREABLE_OBJ_TYPE, id: 'id-4' }; - const objects = [obj1, obj2, obj3, obj4]; - - const setupForAllSpaces = (spacesToAdd: string[], spacesToRemove: string[]) => { - mockSecurityExt = savedObjectsExtensionsMock.createSecurityExtension(); - params = setup({ objects, spacesToAdd, spacesToRemove }, mockSecurityExt); - mockMgetResults( - { found: true, namespaces: [ALL_NAMESPACES_STRING, otherSpace] }, // result for obj1 -- will not be changed - { found: true, namespaces: [ALL_NAMESPACES_STRING] }, // result for obj2 -- will be updated to add otherSpace - { found: true, namespaces: [EXISTING_SPACE, otherSpace] }, // result for obj3 -- will be updated to remove EXISTING_SPACE - { found: true, namespaces: [EXISTING_SPACE] } // result for obj4 -- will be updated to remove EXISTING_SPACE and add otherSpace - ); - mockBulkResults({ error: false }, { error: false }, { error: false }); // results for obj2, obj3, and obj4 - setupPerformAuthFullyAuthorized(mockSecurityExt); - setupRedactPassthrough(mockSecurityExt); - }; - - test(`calls performAuthorization with '*' when spacesToAdd includes '*'`, async () => { - const spacesToAdd = ['*']; - const spacesToRemove = [otherSpace]; - setupForAllSpaces(spacesToAdd, spacesToRemove); - await updateObjectsSpaces(params); + test(`calls redactNamespaces with authorization map`, async () => { + setupAuthorizeFunc(mockSecurityExt.authorizeUpdateSpaces, 'fully_authorized'); + setupRedactPassthrough(mockSecurityExt); - expect(client.bulk).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['share_to_space']); - const expectedSpaces = new Set(['*', defaultSpace, otherSpace, EXISTING_SPACE]); - const expectedTypes = new Set([SHAREABLE_OBJ_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set( - SHAREABLE_OBJ_TYPE, - new Set([defaultSpace, otherSpace, ...spacesToAdd]) - ); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); - }); - - test(`calls performAuthorization with '*' when spacesToRemove includes '*'`, async () => { - const spacesToAdd = [otherSpace]; - const spacesToRemove = ['*']; - setupForAllSpaces(spacesToAdd, spacesToRemove); - await updateObjectsSpaces(params); + await updateObjectsSpaces(params); - expect(client.bulk).toHaveBeenCalledTimes(1); - expect(mockSecurityExt.performAuthorization).toHaveBeenCalledTimes(1); - const expectedActions = new Set(['share_to_space']); - const expectedSpaces = new Set(['*', defaultSpace, otherSpace, EXISTING_SPACE]); - const expectedTypes = new Set([SHAREABLE_OBJ_TYPE]); - const expectedEnforceMap = new Map>(); - expectedEnforceMap.set( - SHAREABLE_OBJ_TYPE, - new Set([defaultSpace, otherSpace, ...spacesToRemove]) - ); + expect(mockSecurityExt.authorizeUpdateSpaces).toHaveBeenCalledTimes(1); + expect(mockSecurityExt.redactNamespaces).toHaveBeenCalledTimes(4); - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = mockSecurityExt.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(actualActions, expectedActions)).toBeTruthy(); - expect(setsAreEqual(actualSpaces, expectedSpaces)).toBeTruthy(); - expect(setsAreEqual(actualTypes, expectedTypes)).toBeTruthy(); - expect(setMapsAreEqual(actualEnforceMap, expectedEnforceMap)).toBeTruthy(); - expect(actualOptions).toEqual(expect.objectContaining({ allowGlobalResource: true })); + objects.forEach((obj, i) => { + expect(mockSecurityExt.redactNamespaces).toHaveBeenNthCalledWith(i + 1, { + savedObject: { + type: obj.type, + namespaces: [...new Set(obj.spaces.concat(spacesToAdd))].filter( + (element) => !spacesToRemove.includes(element) + ), + }, + typeMap: authMap, + }); }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.ts index 5eab945874309..dd6bdc5c3e17d 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/update_objects_spaces.ts @@ -18,23 +18,19 @@ import type { SavedObjectsUpdateObjectsSpacesResponse, SavedObjectsUpdateObjectsSpacesResponseObject, } from '@kbn/core-saved-objects-api-server'; -import { - AuditAction, - type ISavedObjectsSecurityExtension, - type ISavedObjectTypeRegistry, - type SavedObjectsRawDocSource, +import type { + SavedObject, + AuthorizeObjectWithExistingSpaces, + ISavedObjectsSecurityExtension, + ISavedObjectTypeRegistry, + SavedObjectsRawDocSource, } from '@kbn/core-saved-objects-server'; -import { - SavedObjectsErrorHelpers, - ALL_NAMESPACES_STRING, - type DecoratedError, - SavedObjectsUtils, -} from '@kbn/core-saved-objects-utils-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers, type DecoratedError } from '@kbn/core-saved-objects-server'; import type { IndexMapping, SavedObjectsSerializer, } from '@kbn/core-saved-objects-base-server-internal'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import { getBulkOperationError, getExpectedVersionProperties, @@ -180,50 +176,22 @@ export async function updateObjectsSpaces({ throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(); } - const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); - const addToSpaces = spacesToAdd.length ? spacesToAdd : undefined; - const deleteFromSpaces = spacesToRemove.length ? spacesToRemove : undefined; - const typesAndSpaces = new Map>(); - const spacesToAuthorize = new Set(); - for (const { value } of validObjects) { - const { type, esRequestIndex: index } = value; + const authObjects: AuthorizeObjectWithExistingSpaces[] = validObjects.map((element) => { + const { type, id, esRequestIndex: index } = element.value; const preflightResult = index !== undefined ? bulkGetResponse?.body.docs[index] : undefined; + return { + type, + id, + // @ts-expect-error MultiGetHit._source is optional + existingNamespaces: preflightResult?._source?.namespaces ?? [], + }; + }); - const spacesToEnforce = - typesAndSpaces.get(type) ?? new Set([...spacesToAdd, ...spacesToRemove, namespaceString]); // Always enforce authZ for the active space - typesAndSpaces.set(type, spacesToEnforce); - for (const space of spacesToEnforce) { - spacesToAuthorize.add(space); - } - // @ts-expect-error MultiGetHit._source is optional - for (const space of preflightResult?._source?.namespaces ?? []) { - // Existing namespaces are included so we can later redact if necessary - // If this is a specific space, add it to the spaces we'll check privileges for (don't accidentally check for global privileges) - if (space === ALL_NAMESPACES_STRING) continue; - spacesToAuthorize.add(space); - } - } - - const authorizationResult = await securityExtension?.performAuthorization({ - // If a user tries to share/unshare an object to/from '*', they need to have 'share_to_space' privileges for the Global Resource (e.g., - // All privileges for All Spaces). - actions: new Set(['share_to_space']), - types: new Set(typesAndSpaces.keys()), - spaces: spacesToAuthorize, - enforceMap: typesAndSpaces, - auditCallback: (error) => { - for (const { value } of validObjects) { - securityExtension!.addAuditEvent({ - action: AuditAction.UPDATE_OBJECTS_SPACES, - savedObject: { type: value.type, id: value.id }, - addToSpaces, - deleteFromSpaces, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the update operation has not occurred yet - }); - } - }, - options: { allowGlobalResource: true }, + const authorizationResult = await securityExtension?.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects: authObjects, }); const time = new Date().toISOString(); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts index fe775dd0cf945..6f7fb8299291e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/mocks/saved_objects_extensions.mock.ts @@ -20,10 +20,26 @@ const createEncryptionExtension = (): jest.Mocked => ({ - performAuthorization: jest.fn(), - enforceAuthorization: jest.fn(), - addAuditEvent: jest.fn(), + authorizeCreate: jest.fn(), + authorizeBulkCreate: jest.fn(), + authorizeUpdate: jest.fn(), + authorizeBulkUpdate: jest.fn(), + authorizeDelete: jest.fn(), + authorizeBulkDelete: jest.fn(), + authorizeGet: jest.fn(), + authorizeBulkGet: jest.fn(), + authorizeCheckConflicts: jest.fn(), + authorizeRemoveReferences: jest.fn(), + authorizeOpenPointInTime: jest.fn(), + auditClosePointInTime: jest.fn(), + authorizeFind: jest.fn(), + getFindRedactTypeMap: jest.fn(), + authorizeAndRedactMultiNamespaceReferences: jest.fn(), + authorizeAndRedactInternalBulkResolve: jest.fn(), redactNamespaces: jest.fn(), + authorizeUpdateSpaces: jest.fn(), + authorizeDisableLegacyUrlAliases: jest.fn(), + auditObjectsForSpaceDeletion: jest.fn(), }); const createSpacesExtension = (): jest.Mocked => ({ diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts index 1771012ebba33..225b58859a0e6 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/saved_objects_client.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SavedObject } from '@kbn/core-saved-objects-server'; +import { type SavedObject, SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract, ISavedObjectsRepository, @@ -43,7 +43,6 @@ import type { SavedObjectsBulkDeleteOptions, SavedObjectsBulkDeleteResponse, } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; /** * Core internal implementation of {@link SavedObjectsClientContract} diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts index 547084a9d3970..bebd9f9340d0d 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts @@ -10,18 +10,20 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { schema } from '@kbn/config-schema'; import { loggerMock } from '@kbn/logging-mocks'; import { Payload } from 'elastic-apm-node'; -import { +import type { AuthorizationTypeEntry, + AuthorizeAndRedactMultiNamespaceReferencesParams, CheckAuthorizationResult, ISavedObjectsSecurityExtension, - PerformAuthorizationParams, SavedObjectsMappingProperties, SavedObjectsRawDocSource, SavedObjectsType, SavedObjectsTypeMappingDefinition, + SavedObject, + SavedObjectReference, + AuthorizeFindParams, } from '@kbn/core-saved-objects-server'; -import { SavedObject, SavedObjectReference } from '@kbn/core-saved-objects-server'; -import { +import type { SavedObjectsBaseOptions, SavedObjectsBulkCreateObject, SavedObjectsBulkDeleteObject, @@ -34,8 +36,6 @@ import { SavedObjectsFindOptions, SavedObjectsUpdateOptions, } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; - import { encodeHitVersion, SavedObjectsSerializer, @@ -43,9 +43,15 @@ import { } from '@kbn/core-saved-objects-base-server-internal'; import { elasticsearchClientMock, - ElasticsearchClientMock, + type ElasticsearchClientMock, } from '@kbn/core-elasticsearch-client-server-mocks'; import { DocumentMigrator } from '@kbn/core-saved-objects-migration-server-internal'; +import { + AuthorizeAndRedactInternalBulkResolveParams, + GetFindRedactTypeMapParams, + AuthorizationTypeMap, + SavedObjectsErrorHelpers, +} from '@kbn/core-saved-objects-server'; import { mockGetSearchDsl } from '../lib/repository.test.mock'; import { SavedObjectsRepository } from '../lib/repository'; @@ -236,54 +242,80 @@ export const enforceError = SavedObjectsErrorHelpers.decorateForbiddenError( 'User lacks privileges' ); -export const setupPerformAuthFullyAuthorized = ( +// Note: Only using 'any' here because we don't care about/use the method parameters of the mock +// The only alternative I can see is to use a union of all the parameter interfaces which would +// be lengthy. +export const setupAuthorizeFunc = ( + jestMock: jest.MockInstance>, any>, + status: 'fully_authorized' | 'partially_authorized' | 'unauthorized' +) => { + jestMock.mockImplementation((): Promise> => { + if (status === 'unauthorized') throw enforceError; + return Promise.resolve({ status, typeMap: authMap }); + }); +}; + +export const setupAuthorizeFind = ( + mockSecurityExt: jest.Mocked, + status: 'fully_authorized' | 'partially_authorized' | 'unauthorized' +) => { + mockSecurityExt.authorizeFind.mockImplementation( + (params: AuthorizeFindParams): Promise> => { + return Promise.resolve({ status, typeMap: authMap }); + } + ); +}; + +export const setupGetFindRedactTypeMap = ( mockSecurityExt: jest.Mocked ) => { - mockSecurityExt.performAuthorization.mockImplementation( - (params: PerformAuthorizationParams): Promise> => { - const { auditCallback } = params; - auditCallback?.(undefined); - return Promise.resolve({ status: 'fully_authorized', typeMap: authMap }); + mockSecurityExt.getFindRedactTypeMap.mockImplementation( + (params: GetFindRedactTypeMapParams): Promise> => { + return Promise.resolve(authMap); } ); }; -export const setupPerformAuthPartiallyAuthorized = ( +export const setupAuthorizeAndRedactInternalBulkResolveFailure = ( mockSecurityExt: jest.Mocked ) => { - mockSecurityExt.performAuthorization.mockImplementation( - (params: PerformAuthorizationParams): Promise> => { - const { auditCallback } = params; - auditCallback?.(undefined); - return Promise.resolve({ status: 'partially_authorized', typeMap: authMap }); + mockSecurityExt.authorizeAndRedactInternalBulkResolve.mockImplementation( + (params: AuthorizeAndRedactInternalBulkResolveParams) => { + throw enforceError; } ); }; -export const setupPerformAuthUnauthorized = ( +export const setupAuthorizeAndRedactInternalBulkResolveSuccess = ( mockSecurityExt: jest.Mocked ) => { - mockSecurityExt.performAuthorization.mockImplementation( - (params: PerformAuthorizationParams): Promise> => { - const { auditCallback } = params; - auditCallback?.(undefined); - return Promise.resolve({ status: 'unauthorized', typeMap: new Map([]) }); + mockSecurityExt.authorizeAndRedactInternalBulkResolve.mockImplementation( + (params: AuthorizeAndRedactInternalBulkResolveParams) => { + return Promise.resolve(params.objects); } ); }; -export const setupPerformAuthEnforceFailure = ( +export const setupAuthorizeAndRedactMultiNamespaceReferenencesFailure = ( mockSecurityExt: jest.Mocked ) => { - mockSecurityExt.performAuthorization.mockImplementation( - (params: PerformAuthorizationParams) => { - const { auditCallback } = params; - auditCallback?.(enforceError); + mockSecurityExt.authorizeAndRedactMultiNamespaceReferences.mockImplementation( + (params: AuthorizeAndRedactMultiNamespaceReferencesParams) => { throw enforceError; } ); }; +export const setupAuthorizeAndRedactMultiNamespaceReferenencesSuccess = ( + mockSecurityExt: jest.Mocked +) => { + mockSecurityExt.authorizeAndRedactMultiNamespaceReferences.mockImplementation( + (params: AuthorizeAndRedactMultiNamespaceReferencesParams) => { + return Promise.resolve(params.objects); + } + ); +}; + export const setupRedactPassthrough = ( mockSecurityExt: jest.Mocked ) => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_client.mock.ts b/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_client.mock.ts index 523e5003e650f..97480d55c2f27 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_client.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_client.mock.ts @@ -7,7 +7,7 @@ */ import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { savedObjectsPointInTimeFinderMock } from './point_in_time_finder.mock'; const create = () => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts b/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts index fe775dd0cf945..6f7fb8299291e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-mocks/src/saved_objects_extensions.mock.ts @@ -20,10 +20,26 @@ const createEncryptionExtension = (): jest.Mocked => ({ - performAuthorization: jest.fn(), - enforceAuthorization: jest.fn(), - addAuditEvent: jest.fn(), + authorizeCreate: jest.fn(), + authorizeBulkCreate: jest.fn(), + authorizeUpdate: jest.fn(), + authorizeBulkUpdate: jest.fn(), + authorizeDelete: jest.fn(), + authorizeBulkDelete: jest.fn(), + authorizeGet: jest.fn(), + authorizeBulkGet: jest.fn(), + authorizeCheckConflicts: jest.fn(), + authorizeRemoveReferences: jest.fn(), + authorizeOpenPointInTime: jest.fn(), + auditClosePointInTime: jest.fn(), + authorizeFind: jest.fn(), + getFindRedactTypeMap: jest.fn(), + authorizeAndRedactMultiNamespaceReferences: jest.fn(), + authorizeAndRedactInternalBulkResolve: jest.fn(), redactNamespaces: jest.fn(), + authorizeUpdateSpaces: jest.fn(), + authorizeDisableLegacyUrlAliases: jest.fn(), + auditObjectsForSpaceDeletion: jest.fn(), }); const createSpacesExtension = (): jest.Mocked => ({ diff --git a/packages/core/saved-objects/core-saved-objects-api-server-mocks/tsconfig.json b/packages/core/saved-objects/core-saved-objects-api-server-mocks/tsconfig.json index 96548fe6eaacd..ee4feb4555a53 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-mocks/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-api-server-mocks/tsconfig.json @@ -13,7 +13,6 @@ "kbn_references": [ "@kbn/core-saved-objects-api-server", "@kbn/core-saved-objects-api-server-internal", - "@kbn/core-saved-objects-utils-server", "@kbn/logging-mocks", "@kbn/core-saved-objects-server", ], diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts index dd4bdcdc8186f..744d579342af7 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { decodeBase64 } from './base64'; /** diff --git a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts index d7f9b8a8b4d0b..ccef134c59567 100644 --- a/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-browser-internal/src/saved_objects_client.ts @@ -32,7 +32,6 @@ import type { SimpleSavedObject, SavedObjectsBulkDeleteResponse, } from '@kbn/core-saved-objects-api-browser'; - import { SimpleSavedObjectImpl } from './simple_saved_object'; type PromiseType> = T extends Promise ? U : never; diff --git a/packages/core/saved-objects/core-saved-objects-common/index.ts b/packages/core/saved-objects/core-saved-objects-common/index.ts index 3efefa77d52b3..94754d6822005 100644 --- a/packages/core/saved-objects/core-saved-objects-common/index.ts +++ b/packages/core/saved-objects/core-saved-objects-common/index.ts @@ -32,4 +32,4 @@ export type { SavedObjectsImportConflictError, } from './src/saved_objects_imports'; -export type { SavedObjectTypeIdTuple } from './src/types'; +export type { SavedObjectTypeIdTuple, LegacyUrlAliasTarget } from './src/types'; diff --git a/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts b/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts index 8b0ae5c819952..496611aed989e 100644 --- a/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts +++ b/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts @@ -58,6 +58,11 @@ export interface SavedObjectReference { id: string; } +/** + * Definition of the Saved Object interface + * + * @public + */ export interface SavedObject { /** The ID of this Saved Object, guaranteed to be unique for all objects of the same `type` */ id: string; diff --git a/packages/core/saved-objects/core-saved-objects-common/src/types.ts b/packages/core/saved-objects/core-saved-objects-common/src/types.ts index 102de1a4cfbb6..a2658713dbc60 100644 --- a/packages/core/saved-objects/core-saved-objects-common/src/types.ts +++ b/packages/core/saved-objects/core-saved-objects-common/src/types.ts @@ -17,3 +17,22 @@ export interface SavedObjectTypeIdTuple { /** The type of the saved object */ type: string; } + +// NOTE: moved from x-pack/plugins/spaces/common/types.ts for use by SO security ext +/** + * Client interface for interacting with legacy URL aliases. + */ +export interface LegacyUrlAliasTarget { + /** + * The namespace that the object existed in when it was converted. + */ + targetSpace: string; + /** + * The type of the object when it was converted. + */ + targetType: string; + /** + * The original ID of the object, before it was converted. + */ + sourceId: string; +} diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.test.ts index e440b1baffafc..a1f35678ecbdb 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.test.ts @@ -8,10 +8,10 @@ import { httpServerMock } from '@kbn/core-http-server-mocks'; import type { SavedObjectError } from '@kbn/core-saved-objects-common'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsExportTransform, SavedObjectsExportablePredicate, + SavedObject, } from '@kbn/core-saved-objects-server'; import { applyExportTransformsMock } from './collect_exported_objects.test.mocks'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.ts index dddefe8ad73f7..49fc95beb0cc6 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/collect_exported_objects.ts @@ -8,12 +8,12 @@ import type { Logger } from '@kbn/logging'; import type { KibanaRequest } from '@kbn/core-http-server'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import type { SavedObjectsExportablePredicate, ISavedObjectTypeRegistry, SavedObjectsExportTransform, + SavedObject, } from '@kbn/core-saved-objects-server'; import { applyExportTransforms } from './apply_export_transforms'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.ts index 0f7705396f256..f5f735e3b6c5d 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/export/saved_objects_exporter.ts @@ -9,7 +9,6 @@ import type { Readable } from 'stream'; import { createListStream } from '@kbn/utils'; import type { Logger } from '@kbn/logging'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract, SavedObjectsFindResult, @@ -21,6 +20,7 @@ import type { SavedObjectExportBaseOptions, SavedObjectsExportByObjectOptions, SavedObjectsExportByTypeOptions, + SavedObject, } from '@kbn/core-saved-objects-server'; import { sortObjects } from './sort_objects'; import { SavedObjectsExportError } from './errors'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts index a88e0864f532c..66a010b548a4a 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/import_saved_objects.test.ts @@ -23,12 +23,12 @@ import type { SavedObjectsImportFailure, SavedObjectsImportWarning, } from '@kbn/core-saved-objects-common'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import type { SavedObjectsType, ISavedObjectTypeRegistry, SavedObjectsImportHook, + SavedObject, } from '@kbn/core-saved-objects-server'; import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/check_conflicts.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/check_conflicts.test.ts index 13faae72ea25a..0edcb613d4895 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/check_conflicts.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/check_conflicts.test.ts @@ -8,9 +8,12 @@ import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; import type { SavedObjectsImportRetry } from '@kbn/core-saved-objects-common'; -import type { SavedObject, SavedObjectReference } from '@kbn/core-saved-objects-server'; +import { + type SavedObject, + type SavedObjectReference, + SavedObjectsErrorHelpers, +} from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { checkConflicts } from './check_conflicts'; jest.mock('uuid', () => ({ diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/collect_saved_objects.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/collect_saved_objects.ts index 4120aff3ef215..451c74f88d1c2 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/collect_saved_objects.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/collect_saved_objects.ts @@ -13,7 +13,6 @@ import { createMapStream, createPromiseFromStreams, } from '@kbn/utils'; - import type { SavedObjectsImportFailure } from '@kbn/core-saved-objects-common'; import type { SavedObject } from '@kbn/core-saved-objects-server'; import { SavedObjectsImportError } from '../errors'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.test.ts index 9e332f1da896e..7930359433bb3 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.test.ts @@ -8,9 +8,8 @@ import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; import type { SavedObjectsImportFailure } from '@kbn/core-saved-objects-common'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; +import { type SavedObject, SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { createSavedObjects } from './create_saved_objects'; import { extractErrors } from './extract_errors'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.ts index 6276902e7b3a2..6b77c6e2ba2f0 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/create_saved_objects.ts @@ -7,9 +7,8 @@ */ import type { SavedObjectsImportFailure } from '@kbn/core-saved-objects-common'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import type { CreatedObject } from '@kbn/core-saved-objects-server'; +import type { CreatedObject, SavedObject } from '@kbn/core-saved-objects-server'; import { extractErrors } from './extract_errors'; import type { ImportStateMap } from './types'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.test.ts index d677205526113..0f5b969d0b7b1 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.test.ts @@ -7,8 +7,7 @@ */ import type { SavedObjectsImportWarning } from '@kbn/core-saved-objects-common'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; -import type { SavedObjectsImportHookResult } from '@kbn/core-saved-objects-server'; +import type { SavedObject, SavedObjectsImportHookResult } from '@kbn/core-saved-objects-server'; import { executeImportHooks } from './execute_import_hooks'; const createObject = (type: string, id: string): SavedObject => ({ diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.ts index c2ae2ead97ee5..670b13286b604 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/execute_import_hooks.ts @@ -7,8 +7,7 @@ */ import type { SavedObjectsImportWarning } from '@kbn/core-saved-objects-common'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; -import type { SavedObjectsImportHook } from '@kbn/core-saved-objects-server'; +import type { SavedObject, SavedObjectsImportHook } from '@kbn/core-saved-objects-server'; export interface ExecuteImportHooksOptions { objects: SavedObject[]; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.test.ts index 4ce5ac5d82d85..7cf8fc9b181f6 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.test.ts @@ -6,8 +6,11 @@ * Side Public License, v 1. */ -import type { SavedObject, CreatedObject } from '@kbn/core-saved-objects-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { + type SavedObject, + type CreatedObject, + SavedObjectsErrorHelpers, +} from '@kbn/core-saved-objects-server'; import { extractErrors } from './extract_errors'; describe('extractErrors()', () => { diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.ts index a5124f269daa6..3f2a600700de3 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/extract_errors.ts @@ -7,8 +7,7 @@ */ import type { SavedObjectsImportFailure } from '@kbn/core-saved-objects-common'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; -import type { CreatedObject } from '@kbn/core-saved-objects-server'; +import type { CreatedObject, SavedObject } from '@kbn/core-saved-objects-server'; export function extractErrors( // TODO: define saved object type diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.test.ts index c0f0e510e1bb0..7b0313198a3d1 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.test.ts @@ -8,6 +8,7 @@ import type { SavedObjectsImportRetry } from '@kbn/core-saved-objects-common'; import type { SavedObject } from '@kbn/core-saved-objects-server'; + import { getImportStateMapForRetries } from './get_import_state_map_for_retries'; describe('#getImportStateMapForRetries', () => { diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.ts index 65d30b040b2b3..8be358e9189c5 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/get_import_state_map_for_retries.ts @@ -8,6 +8,7 @@ import type { SavedObjectsImportRetry } from '@kbn/core-saved-objects-common'; import type { SavedObject } from '@kbn/core-saved-objects-server'; + import type { ImportStateMap } from './types'; interface GetImportStateMapForRetriesParams { diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts index ceced695b2870..d46382a09b766 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { ValidateReferencesParams } from './validate_references'; import { validateReferences } from './validate_references'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.test.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.test.ts index e1cafa319eaf9..70d976f335e04 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.test.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.test.ts @@ -28,12 +28,13 @@ import type { SavedObjectsImportRetry, SavedObjectsImportWarning, } from '@kbn/core-saved-objects-common'; -import type { SavedObject, SavedObjectReference } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import type { SavedObjectsType, ISavedObjectTypeRegistry, SavedObjectsImportHook, + SavedObject, + SavedObjectReference, } from '@kbn/core-saved-objects-server'; import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.ts index d6876492d11f3..7ffa0efd31b8b 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/resolve_import_errors.ts @@ -13,11 +13,11 @@ import type { SavedObjectsImportResponse, SavedObjectsImportSuccess, } from '@kbn/core-saved-objects-common'; -import { SavedObject } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import type { ISavedObjectTypeRegistry, SavedObjectsImportHook, + SavedObject, } from '@kbn/core-saved-objects-server'; import { collectSavedObjects, diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/tsconfig.json index 0cd74a160792a..4343a769ef99c 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/tsconfig.json @@ -23,7 +23,6 @@ "@kbn/logging-mocks", "@kbn/core-saved-objects-base-server-mocks", "@kbn/core-http-router-server-internal", - "@kbn/core-saved-objects-utils-server", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts index 1faaba0f5b710..3d86f9247c2d5 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts +++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/utils.ts @@ -17,12 +17,12 @@ import { } from '@kbn/utils'; import Boom from '@hapi/boom'; import type { RequestHandlerWrapper } from '@kbn/core-http-server'; -import type { +import { SavedObject, ISavedObjectTypeRegistry, SavedObjectsExportResultDetails, + SavedObjectsErrorHelpers, } from '@kbn/core-saved-objects-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; export async function createSavedObjectsStreamFromNdJson(ndJsonStream: Readable) { const savedObjects = await createPromiseFromStreams([ diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json index 6d9f8874ebdb4..cfe782040bf6d 100644 --- a/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-server-internal/tsconfig.json @@ -45,7 +45,6 @@ "@kbn/core-elasticsearch-server-mocks", "@kbn/utils", "@kbn/core-http-router-server-internal", - "@kbn/core-saved-objects-utils-server", ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-server/index.ts b/packages/core/saved-objects/core-saved-objects-server/index.ts index 2e6d3837cfac6..e3a56f4ce0529 100644 --- a/packages/core/saved-objects/core-saved-objects-server/index.ts +++ b/packages/core/saved-objects/core-saved-objects-server/index.ts @@ -71,17 +71,31 @@ export type { EncryptedObjectDescriptor, } from './src/extensions/encryption'; export type { - CheckAuthorizationParams, - PerformAuthorizationParams, AuthorizationTypeEntry, AuthorizationTypeMap, CheckAuthorizationResult, - EnforceAuthorizationParams, - AddAuditEventParams, RedactNamespacesParams, ISavedObjectsSecurityExtension, + AuthorizeCreateObject, + AuthorizeUpdateObject, + AuthorizeBulkGetObject, + AuthorizeCreateParams, + AuthorizeUpdateParams, + AuthorizeAndRedactMultiNamespaceReferencesParams, + AuthorizeAndRedactInternalBulkResolveParams, + AuthorizeGetParams, + AuthorizeBulkGetParams, + AuthorizeObjectWithExistingSpaces, + AuthorizeBulkCreateParams, + AuthorizeBulkDeleteParams, + AuthorizeBulkUpdateParams, + AuthorizeCheckConflictsParams, + AuthorizeDeleteParams, + GetFindRedactTypeMapParams, + AuthorizeOpenPointInTimeParams, + AuthorizeUpdateSpacesParams, + AuthorizeFindParams, } from './src/extensions/security'; -export { AuditAction } from './src/extensions/security'; export type { ISavedObjectsSpacesExtension } from './src/extensions/spaces'; export type { SavedObjectsExtensions } from './src/extensions/extensions'; export { @@ -89,6 +103,12 @@ export { SECURITY_EXTENSION_ID, SPACES_EXTENSION_ID, } from './src/extensions/extensions'; +export { + SavedObjectsErrorHelpers, + type DecoratedError, + type BulkResolveError, +} from './src/saved_objects_error_helpers'; + export type { SavedObjectsModelVersion, SavedObjectsModelVersionMap, diff --git a/packages/core/saved-objects/core-saved-objects-server/src/extensions/extensions.ts b/packages/core/saved-objects/core-saved-objects-server/src/extensions/extensions.ts index f94af85d4ae26..143bdf33026c3 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/extensions/extensions.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/extensions/extensions.ts @@ -17,8 +17,11 @@ import type { ISavedObjectsSpacesExtension } from './spaces'; * security, and spaces features. */ export interface SavedObjectsExtensions { + /** The encryption extension - handles encrypting and decrypting attributes of saved objects */ encryptionExtension?: ISavedObjectsEncryptionExtension; + /** The security extension - handles action authorization, audit logging, and space redaction */ securityExtension?: ISavedObjectsSecurityExtension; + /** The spaces extension - handles retrieving the current space and retrieving available spaces */ spacesExtension?: ISavedObjectsSpacesExtension; } diff --git a/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts b/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts index 1805e79b74340..68637be38b6f2 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts @@ -6,70 +6,13 @@ * Side Public License, v 1. */ -import type { SavedObject } from '@kbn/core-saved-objects-common'; -import type { EcsEvent } from '@kbn/ecs'; - -/** - * The PerformAuthorizationParams interface contains settings for checking - * & enforcing authorization via the ISavedObjectsSecurityExtension. - */ -export interface PerformAuthorizationParams
{ - /** - * A set of actions to check. - */ - actions: Set; - /** - * A set of types to check. - */ - types: Set; - /** - * A set of spaces to check (types to check comes from the typesAndSpaces map). - */ - spaces: Set; - /** - * A map of types (key) to spaces (value) that will be affected by the action(s). - * If undefined, enforce with be bypassed. - */ - enforceMap?: Map>; - /** - * A callback intended to handle adding audit events in - * both error (unauthorized), or success (authorized) - * cases - */ - auditCallback?: (error?: Error) => void; - /** - * Authorization options - * allowGlobalResource - whether or not to allow global resources, false if options are undefined - */ - options?: { - allowGlobalResource: boolean; - }; -} - -/** - * The CheckAuthorizationParams interface contains settings for checking - * authorization via the ISavedObjectsSecurityExtension. - */ -export interface CheckAuthorizationParams { - /** - * A set of types to check. - */ - types: Set; - /** - * A set of spaces to check. - */ - spaces: Set; - /** - * An set of actions to check. - */ - actions: Set; - /** - * Authorization options - whether or not to allow global resources, false if options are undefined - */ - options?: { - allowGlobalResource: boolean; - }; -} +import { + SavedObjectReferenceWithContext, + SavedObjectsFindResult, + SavedObjectsResolveResponse, +} from '@kbn/core-saved-objects-api-server'; +import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; +import { SavedObject, BulkResolveError } from '../..'; /** * The AuthorizationTypeEntry interface contains space-related details @@ -112,84 +55,254 @@ export interface CheckAuthorizationResult { } /** - * The EnforceAuthorizationParams interface contains settings for - * enforcing a single action via the ISavedObjectsSecurityExtension. + * The AuthorizeObject interface contains information to specify an + * object for authorization. This is a base interface which is + * extended by other interfaces for specific actions. */ -export interface EnforceAuthorizationParams { - /** - * A map of types to spaces that will be affected by the action - */ - typesAndSpaces: Map>; +export interface AuthorizeObject { + /** The type of object */ + type: string; + /** The id of the object */ + id: string; +} + +/** + * The AuthorizeObjectWithExistingSpaces extends AuthorizeObject and contains + * an array of existing namespaces for the object. Used by the + * authorizeDelete, authorizeBulkDelete, authorizeGet, + * authorizeCheckConflicts, and getFindRedactTypeMap methods. + */ +export interface AuthorizeObjectWithExistingSpaces extends AuthorizeObject { /** - * The relevant action (create, update, etc.) + * Spaces where the object is known to exist. Usually populated + * by document data from the result of an es query. */ - action: A; + existingNamespaces: string[]; +} + +/** + * The AuthorizeBulkGetObject interface extends AuthorizeObjectWithExistingSpaces + * and contains a object namespaces override. Used by the + * authorizeBulkGet method. + */ +export interface AuthorizeBulkGetObject extends AuthorizeObjectWithExistingSpaces { /** - * The authorization map from CheckAuthorizationResult: a - * map of type to record of action/AuthorizationTypeEntry - * (spaces/globallyAuthz'd) + * The namespaces to include when retrieving this object. Populated by options + * passed to the repository's update or bulkUpdate method. */ - typeMap: AuthorizationTypeMap; + objectNamespaces?: string[]; /** - * A callback intended to handle adding audit events in - * both error (unauthorized), or success (authorized) - * cases + * Whether or not an error occurred when getting this object. Populated by + * the result of a query. Default is false. */ - auditCallback?: (error?: Error) => void; + error?: boolean; } /** - * The AuditAction enumeration contains values for all - * valid audit actions for use in AddAuditEventParams. + * The AuthorizeParams interface is a base interface for parameters to several + * public authorize methods within the ISavedObjectsSecurityExtension. */ -export enum AuditAction { - CREATE = 'saved_object_create', - GET = 'saved_object_get', - RESOLVE = 'saved_object_resolve', - UPDATE = 'saved_object_update', - DELETE = 'saved_object_delete', - FIND = 'saved_object_find', - REMOVE_REFERENCES = 'saved_object_remove_references', - OPEN_POINT_IN_TIME = 'saved_object_open_point_in_time', - CLOSE_POINT_IN_TIME = 'saved_object_close_point_in_time', - COLLECT_MULTINAMESPACE_REFERENCES = 'saved_object_collect_multinamespace_references', // this is separate from 'saved_object_get' because the user is only accessing an object's metadata - UPDATE_OBJECTS_SPACES = 'saved_object_update_objects_spaces', // this is separate from 'saved_object_update' because the user is only updating an object's metadata +export interface AuthorizeParams { + /** + * The namespace in which to perform the authorization operation. + * If undefined, the current space will be used unless spaces are disabled, + * in which case the default space will be used. + */ + namespace: string | undefined; } /** - * The AddAuditEventParams interface contains settings for adding - * audit events via the ISavedObjectsSecurityExtension. + * The AuthorizeCreateObject interface extends AuthorizeObjectWithExistingSpaces + * and contains an array of initial namespaces for the object. Used by + * the authorizeCreate and authorizeBulkCreate methods. */ -export interface AddAuditEventParams { +export interface AuthorizeCreateObject extends AuthorizeObjectWithExistingSpaces { /** - * The relevant action + * Initial spaces to include the created object. Populated by options + * passed to the repository's bulkCreate method. */ - action: AuditAction; + initialNamespaces?: string[]; +} + +/** + * The AuthorizeUpdateObject interface extends AuthorizeObjectWithExistingSpaces + * and contains a object namespace override. Used by the authorizeUpdate + * and authorizeBulkUpdate methods. + */ +export interface AuthorizeUpdateObject extends AuthorizeObjectWithExistingSpaces { /** - * The outcome of the operation - * 'failure' | 'success' | 'unknown' + * The namespace in which to update this object. Populated by options + * passed to the repository's update or bulkUpdate method. */ - outcome?: EcsEvent['outcome']; + objectNamespace?: string; +} + +/** + * The MultiNamespaceReferencesOptions interface contains options + * specific for authorizing CollectMultiNamespaceReferences actions. + */ +export interface MultiNamespaceReferencesOptions { /** - * relevant saved object information - * object containing type & id strings + * The purpose of the call to 'collectMultiNamespaceReferences'. + * Default is 'collectMultiNamespaceReferences'. */ - savedObject?: { type: string; id: string }; + purpose?: 'collectMultiNamespaceReferences' | 'updateObjectsSpaces'; +} + +/** + * The AuthorizeCreateParams interface extends AuthorizeParams and is + * used for the AuthorizeCreate method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeCreateParams extends AuthorizeParams { + /** The object to authorize */ + object: AuthorizeCreateObject; +} + +/** + * The AuthorizeBulkCreateParams interface extends AuthorizeParams and is + * used for the AuthorizeBulkCreate method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeBulkCreateParams extends AuthorizeParams { + /** The objects to authorize */ + objects: AuthorizeCreateObject[]; +} + +/** + * The AuthorizeUpdateParams interface extends AuthorizeParams and is + * used for the AuthorizeUpdate method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeUpdateParams extends AuthorizeParams { + /** The object to authorize */ + object: AuthorizeUpdateObject; +} + +/** + * The AuthorizeBulkUpdateParams interface extends AuthorizeParams and is + * used for the AuthorizeBulkUpdate method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeBulkUpdateParams extends AuthorizeParams { + /** The objects to authorize */ + objects: AuthorizeUpdateObject[]; +} + +/** + * The AuthorizeDeleteParams interface extends AuthorizeParams and is + * used for the AuthorizeDelete method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeDeleteParams extends AuthorizeParams { + /** The object to authorize */ + object: AuthorizeObject; +} + +/** + * The AuthorizeBulkDeleteParams interface extends AuthorizeParams and is + * used for the AuthorizeBulkDelete method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeBulkDeleteParams extends AuthorizeParams { + /** The objects to authorize */ + objects: AuthorizeObjectWithExistingSpaces[]; +} + +/** + * The AuthorizeGetParams interface extends AuthorizeParams and is + * used for the AuthorizeGet method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeGetParams extends AuthorizeParams { + /** The object to authorize */ + object: AuthorizeObjectWithExistingSpaces; + /** Whether or not the object was not found, defaults to false */ + objectNotFound?: boolean; +} + +/** + * The AuthorizeBulkGetParams interface extends AuthorizeParams and is + * used for the AuthorizeBulkGet method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeBulkGetParams extends AuthorizeParams { + /** The objects to authorize */ + objects: AuthorizeBulkGetObject[]; +} + +/** + * The AuthorizeCheckConflictsParams interface extends AuthorizeParams and is + * used for the AuthorizeCheckConflicts method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeCheckConflictsParams extends AuthorizeParams { + /** The objects to authorize */ + objects: AuthorizeObject[]; +} + +/** + * The AuthorizeFindParams interface is used for the AuthorizeFind method + * of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeFindParams { + /** The namespaces in which to find objects */ + namespaces: Set; + /** The types of objects to find */ + types: Set; +} + +/** + * The AuthorizeOpenPointInTimeParams interface is used for the + * AuthorizeOpenPointInTime method of the ISavedObjectsSecurityExtension. + * It is identical to AuthorizeFindParams. + */ +export type AuthorizeOpenPointInTimeParams = AuthorizeFindParams; + +/** + * The AuthorizeAndRedactMultiNamespaceReferencesParams interface extends + * AuthorizeParams and is used for the AuthorizeAndRedactMultiNamespaceReferences + * method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeAndRedactMultiNamespaceReferencesParams extends AuthorizeParams { + /** The objects to authorize */ + objects: SavedObjectReferenceWithContext[]; /** - * Array of spaces being added. For - * UPDATE_OBJECTS_SPACES action only + * options for the operation + * - purpose: 'collectMultiNamespaceReferences' or 'updateObjectsSpaces' + * default purpose is 'collectMultiNamespaceReferences'. */ - addToSpaces?: readonly string[]; + options?: MultiNamespaceReferencesOptions; +} + +/** + * The AuthorizeAndRedactInternalBulkResolveParams interface extends + * AuthorizeParams and is used for the AuthorizeAndRedactInternalBulkResolve + * method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeAndRedactInternalBulkResolveParams extends AuthorizeParams { /** - * Array of spaces being removed. For - * UPDATE_OBJECTS_SPACES action only + * The objects to authorize */ - deleteFromSpaces?: readonly string[]; + objects: Array | BulkResolveError>; +} + +/** + * The GetFindRedactTypeMapParams interface is used for the GetFindRedactTypeMap + * method of the ISavedObjectsSecurityExtension. + */ +export interface GetFindRedactTypeMapParams { + /** The namespaces previously checked by the AuthorizeFind method */ + previouslyCheckedNamespaces: Set; /** - * relevant error information to add to - * the audit event + * The objects to authorize in order to generate the type map + * this should be populated by the result of the es query */ - error?: Error; + objects: AuthorizeObjectWithExistingSpaces[]; +} + +/** + * The AuthorizeUpdateSpacesParams interface extends AuthorizeParams and is + * used for the AuthorizeUpdateSpaces method of the ISavedObjectsSecurityExtension. + */ +export interface AuthorizeUpdateSpacesParams extends AuthorizeParams { + /** The spaces in which to add the objects */ + spacesToAdd: string[]; + /** The spaces from which to remove the objects */ + spacesToRemove: string[]; + /** The objects to authorize */ + objects: AuthorizeObjectWithExistingSpaces[]; } /** @@ -197,9 +310,7 @@ export interface AddAuditEventParams { * namespace access via the ISavedObjectsSecurityExtension. */ export interface RedactNamespacesParams { - /** - * relevant saved object - */ + /** Relevant saved object */ savedObject: SavedObject; /** * The authorization map from CheckAuthorizationResult: a map of @@ -215,26 +326,164 @@ export interface RedactNamespacesParams { */ export interface ISavedObjectsSecurityExtension { /** - * Performs authorization (check & enforce) of actions on specified types in specified spaces. - * @param params - actions, types & spaces map, audit callback, options (enforce bypassed if enforce map is undefined) + * Performs authorization for the CREATE security action + * @param params the namespace and object to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeCreate: ( + params: AuthorizeCreateParams + ) => Promise>; + + /** + * Performs authorization for the BULK_CREATE security action + * @param params the namespace and objects to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeBulkCreate: ( + params: AuthorizeBulkCreateParams + ) => Promise>; + + /** + * Performs authorization for the UPDATE security action + * @param params the namespace and object to authorize * @returns CheckAuthorizationResult - the resulting authorization level and authorization map */ - performAuthorization: ( - params: PerformAuthorizationParams - ) => Promise>; + authorizeUpdate: ( + params: AuthorizeUpdateParams + ) => Promise>; /** - * Enforces authorization of a single action on specified types in specified spaces. - * Throws error if authorization map does not cover specified parameters. - * @param params - map of types/spaces, action to check, and authz map (from CheckAuthorizationResult) + * Performs authorization for the BULK_UPDATE security action + * @param params the namespace and objects to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map */ - enforceAuthorization: (params: EnforceAuthorizationParams) => void; + authorizeBulkUpdate: ( + params: AuthorizeBulkUpdateParams + ) => Promise>; /** - * Adds an audit event for the specified action with relevant information - * @param params - the action, outcome, error, and relevant object/space information + * Performs authorization for the DELETE security action + * @param params the namespace and object to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map */ - addAuditEvent: (params: AddAuditEventParams) => void; + authorizeDelete: ( + params: AuthorizeDeleteParams + ) => Promise>; + + /** + * Performs authorization for the BULK_DELETE security action + * @param params the namespace and objects to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeBulkDelete: ( + params: AuthorizeBulkDeleteParams + ) => Promise>; + + /** + * Performs authorization for the GET security action + * @param params the namespace, object to authorize, and whether or not the object was found + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeGet: ( + params: AuthorizeGetParams + ) => Promise>; + + /** + * Performs authorization for the BULK_GET security action + * @param params the namespace and objects to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeBulkGet: ( + params: AuthorizeBulkGetParams + ) => Promise>; + + /** + * Performs authorization for the CHECK_CONFLICTS security action + * @param params the namespace and objects to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeCheckConflicts: ( + params: AuthorizeCheckConflictsParams + ) => Promise>; + + /** + * Performs authorization for the REMOVE_REFERENCES security action + * @param params the namespace and object to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeRemoveReferences: ( + params: AuthorizeDeleteParams + ) => Promise>; + + /** + * Performs authorization for the OPEN_POINT_IN_TIME security action + * @param params the namespaces and types to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeOpenPointInTime: ( + params: AuthorizeOpenPointInTimeParams + ) => Promise>; + + /** + * Performs audit logging for the CLOSE_POINT_IN_TIME security action + */ + auditClosePointInTime: () => void; + + /** + * Handles all security operations for the COLLECT_MULTINAMESPACE_REFERENCES security action + * Checks/enforces authorization, writes audit events, filters the object graph, and redacts spaces from the share_to_space/bulk_get + * response. In other SavedObjectsRepository functions we do this before decrypting attributes. However, because of the + * share_to_space/bulk_get response logic involved in deciding between the exact match or alias match, it's cleaner to do authorization, + * auditing, filtering, and redaction all afterwards. + * @param params - the namespace, objects to authorize, and purpose of the operation + * @returns SavedObjectReferenceWithContext[] - array of collected references + */ + authorizeAndRedactMultiNamespaceReferences: ( + params: AuthorizeAndRedactMultiNamespaceReferencesParams + ) => Promise; + + /** + * Handles all security operations for the INTERNAL_BULK_RESOLVE security action + * Checks authorization, writes audit events, and redacts namespaces from the bulkResolve response. In other SavedObjectsRepository + * functions we do this before decrypting attributes. However, because of the bulkResolve logic involved in deciding between the exact match + * or alias match, it's cleaner to do authorization, auditing, and redaction all afterwards. + * @param params - the namespace and objects to authorize + * @returns Array of SavedObjectsResolveResponses or BulkResolveErrors - the redacted resolve responses or errors + */ + authorizeAndRedactInternalBulkResolve: ( + params: AuthorizeAndRedactInternalBulkResolveParams + ) => Promise | BulkResolveError>>; + + /** + * Performs authorization for the UPDATE_OBJECTS_SPACES security action + * @param params - namespace, spacesToAdd, spacesToRemove, and objects to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeUpdateSpaces: ( + params: AuthorizeUpdateSpacesParams + ) => Promise>; + + /** + * Performs authorization for the FIND security action + * This method is the first of two security steps for the find operation (saved objects repository's find method) + * This method should be called first in order to provide data needed to construct the type-to-namespace map for the search DSL + * @param params - namespaces and types to authorize + * @returns CheckAuthorizationResult - the resulting authorization level and authorization map + */ + authorizeFind: ( + params: AuthorizeFindParams + ) => Promise>; + + /** + * Gets an updated type map for redacting results of the FIND security action + * This method is the second of two security steps for the find operation (saved objects repository's find method) + * This method should be called last in order to update the type map used to redact namespaces in the results + * @param params - namespace, spacesToAdd, spacesToRemove, and objects to authorize + * @returns - the updated type map used for redaction + */ + getFindRedactTypeMap: ( + params: GetFindRedactTypeMapParams + ) => Promise | undefined>; /** * Filters a saved object's spaces based on an authorization map (from CheckAuthorizationResult) @@ -242,4 +491,21 @@ export interface ISavedObjectsSecurityExtension { * @returns SavedObject - saved object with filtered spaces */ redactNamespaces: (params: RedactNamespacesParams) => SavedObject; + + /** + * Performs authorization for the disableLegacyUrlAliases method of the SecureSpacesClientWrapper + * There is no return for this method. If unauthorized the method with throw, otherwise will resolve. + * @param aliases - array of legacy url alias targets + */ + authorizeDisableLegacyUrlAliases: (aliases: LegacyUrlAliasTarget[]) => void; + + /** + * Performs saved object audit logging for the delete method of the SecureSpacesClientWrapper + * @param spaceId - the id of the space being deleted + * @param objects - the objects to audit + */ + auditObjectsForSpaceDeletion: ( + spaceId: string, + objects: Array> + ) => void; } diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.test.ts b/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.test.ts similarity index 100% rename from packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.test.ts rename to packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.test.ts diff --git a/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts b/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts new file mode 100644 index 0000000000000..13ce1838c4cd5 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts @@ -0,0 +1,448 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Boom from '@hapi/boom'; + +// 400 - badRequest +const CODE_BAD_REQUEST = 'SavedObjectsClient/badRequest'; +// 400 - invalid version +const CODE_INVALID_VERSION = 'SavedObjectsClient/invalidVersion'; +// 401 - Not Authorized +const CODE_NOT_AUTHORIZED = 'SavedObjectsClient/notAuthorized'; +// 403 - Forbidden +const CODE_FORBIDDEN = 'SavedObjectsClient/forbidden'; +// 413 - Request Entity Too Large +const CODE_REQUEST_ENTITY_TOO_LARGE = 'SavedObjectsClient/requestEntityTooLarge'; +// 404 - Not Found +const CODE_NOT_FOUND = 'SavedObjectsClient/notFound'; +// 409 - Conflict +const CODE_CONFLICT = 'SavedObjectsClient/conflict'; +// 429 - Too Many Requests +const CODE_TOO_MANY_REQUESTS = 'SavedObjectsClient/tooManyRequests'; +// 400 - Es Cannot Execute Script +const CODE_ES_CANNOT_EXECUTE_SCRIPT = 'SavedObjectsClient/esCannotExecuteScript'; +// 503 - Es Unavailable +const CODE_ES_UNAVAILABLE = 'SavedObjectsClient/esUnavailable'; +// 500 - General Error +const CODE_GENERAL_ERROR = 'SavedObjectsClient/generalError'; + +const code = Symbol('SavedObjectsClientErrorCode'); + +/** + * The DecoratedError interface extends the Boom error object + * and augments it with the 'SavedObjectsClientErrorCode' symbol + * property. + */ +export interface DecoratedError extends Boom.Boom { + /** the 'SavedObjectsClientErrorCode' symbol */ + [code]?: string; +} + +/** + * Error result for the internal bulk resolve method. + */ +export interface BulkResolveError { + /** The type of the saved object */ + type: string; + /** The id of the saved object */ + id: string; + /** The decorated resolve error */ + error: DecoratedError; +} + +/** + * Decorates an error - adds information or additional explanation of an error to + * provide more context. + * + */ +function decorate( + error: Error | DecoratedError, + errorCode: string, + statusCode: number, + message?: string +): DecoratedError { + if (isSavedObjectsClientError(error)) { + return error; + } + + const boom = Boom.boomify(error, { + statusCode, + message, + override: false, + }) as DecoratedError; + + boom[code] = errorCode; + + return boom; +} + +/** + * Determines if an error is a saved objects client error + */ +function isSavedObjectsClientError(error: any): error is DecoratedError { + return Boolean(error && error[code]); +} + +/** + * Decorates an bad request error to add information or additional explanation of an error to + * provide more context. Bad requests come in a few flavors: unsupported type, invalid version, + * elastic search cannot execute script, or plain vanilla bad request. + */ +function decorateBadRequestError(error: Error, reason?: string) { + return decorate(error, CODE_BAD_REQUEST, 400, reason); +} + +/** + * The SavedObjectsErrorHelpers class is a simple class for creating, decorating, and + * qualifying saved object errors. + * @public + */ +export class SavedObjectsErrorHelpers { + /** + * Determines if an error is a saved objects client error + * @public + * @param error the error to check + * @returns boolean - true if error is a saved objects client error + */ + public static isSavedObjectsClientError(error: any): error is DecoratedError { + return isSavedObjectsClientError(error); + } + + /** + * Decorates a bad request error (400) by adding a reason + * @public + * @param error the error to decorate + * @param reason the reason for the bad request (optional) + * @returns the decorated error + */ + public static decorateBadRequestError(error: Error, reason?: string) { + return decorateBadRequestError(error, reason); + } + + /** + * Creates a decorated bad request error (400). Bad requests come in a few flavors: + * unsupported type, invalid version, elastic search cannot execute script, or plain + * vanilla bad request. + * @public + * @param reason the reason for the bad request (optional) + * @returns the decorated error + */ + public static createBadRequestError(reason?: string) { + return decorateBadRequestError(new Error('Bad Request'), reason); + } + + /** + * Creates a decorated unsupported type error (flavor of bad request 400) + * @public + * @param type the unsupported saved object type + * @returns the decorated error + */ + public static createUnsupportedTypeError(type: string) { + return decorateBadRequestError( + new Error('Bad Request'), + `Unsupported saved object type: '${type}'` + ); + } + + /** + * Determines if an error is a bad request error (400) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a bad request error + */ + public static isBadRequestError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_BAD_REQUEST; + } + + /** + * Creates a decorated invalid version error (flavor of bad request 400) + * @public + * @param versionInput the version string (optional) + * @returns the decorated error + */ + public static createInvalidVersionError(versionInput?: string) { + return decorate( + Boom.badRequest(`Invalid version [${versionInput}]`), + CODE_INVALID_VERSION, + 400 + ); + } + + /** + * Determines if an error is an invalid version error (flavor of bad request 400) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is an invalid version error + */ + public static isInvalidVersionError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_INVALID_VERSION; + } + + /** + * Decorates an error as an not authorized error (401) + * @public + * @param error the error to decorate + * @param reason the reason for the not authorized error (optional) + * @returns the decorated error + */ + public static decorateNotAuthorizedError(error: Error, reason?: string) { + return decorate(error, CODE_NOT_AUTHORIZED, 401, reason); + } + + /** + * Determines if an error is a not authorized error (401) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a not authorized error + */ + public static isNotAuthorizedError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_NOT_AUTHORIZED; + } + + /** + * Decorates an error as a forbidden error (403) + * @public + * @param error the error to decorate + * @param reason the reason for the forbidden error (optional) + * @returns the decorated error + */ + public static decorateForbiddenError(error: Error, reason?: string) { + return decorate(error, CODE_FORBIDDEN, 403, reason); + } + + /** + * Determines if an error is a forbidden error (403) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a forbidden error + */ + public static isForbiddenError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_FORBIDDEN; + } + + /** + * Decorates a request entity too large error (413) + * @public + * @param error the error to decorate + * @param reason the reason for the request entity too large error + * @returns the decorated error + */ + public static decorateRequestEntityTooLargeError(error: Error, reason?: string) { + return decorate(error, CODE_REQUEST_ENTITY_TOO_LARGE, 413, reason); + } + + /** + * Determines if an error is a request entity too large error(413) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a request entity too large error + */ + public static isRequestEntityTooLargeError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_REQUEST_ENTITY_TOO_LARGE; + } + + /** + * Creates a generic not found error (404) + * @public + * @param type the saved object type or null (default is null) + * @param id the saved object id or null (default is null) + * @returns the decorated error + */ + public static createGenericNotFoundError(type: string | null = null, id: string | null = null) { + if (type && id) { + return decorate(Boom.notFound(`Saved object [${type}/${id}] not found`), CODE_NOT_FOUND, 404); + } + return decorate(Boom.notFound(), CODE_NOT_FOUND, 404); + } + + /** + * Creates an alias not found error (flavor of general error 500) + * @public + * @param alias the unfound saved object alias + * @returns the decorated error + */ + public static createIndexAliasNotFoundError(alias: string) { + return SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError(Boom.internal(), alias); + } + + /** + * Decorates an index alias not found error (flavor of general error 500) + * @public + * @param error the error to decorate + * @param alias the unfound index alias + * @returns the decorated error + */ + public static decorateIndexAliasNotFoundError(error: Error, alias: string) { + return decorate( + error, + CODE_GENERAL_ERROR, + 500, + `Saved object index alias [${alias}] not found` + ); + } + + /** + * Determines if an error is a not found error (404) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a not found error + */ + public static isNotFoundError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_NOT_FOUND; + } + + /** + * Decorates a conflict error (409) + * @public + * @param error the error to decorate + * @param reason the reason for the conflict error (optional) + * @returns the decorated error + */ + public static decorateConflictError(error: Error, reason?: string) { + return decorate(error, CODE_CONFLICT, 409, reason); + } + + /** + * Creates a conflict error (409) + * @public + * @param type the saved object type + * @param id the saved object id + * @param reason the reason for the conflict error (optional) + * @returns the decorated error + */ + public static createConflictError(type: string, id: string, reason?: string) { + return SavedObjectsErrorHelpers.decorateConflictError( + Boom.conflict(`Saved object [${type}/${id}] conflict`), + reason + ); + } + + /** + * Determines if an error is a conflict error (409) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a conflict error + */ + public static isConflictError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_CONFLICT; + } + + /** + * Decorates a too many requests error (429) + * @public + * @param error the error to decorate + * @param reason the reason for the too many requests error (optional) + * @returns the decorated error + */ + public static decorateTooManyRequestsError(error: Error, reason?: string) { + return decorate(error, CODE_TOO_MANY_REQUESTS, 429, reason); + } + + /** + * Creates a too many requests error (429) + * @public + * @param type the saved object type + * @param id the saved object id + * @returns the decorated error + */ + public static createTooManyRequestsError(type: string, id: string) { + return SavedObjectsErrorHelpers.decorateTooManyRequestsError(Boom.tooManyRequests()); + } + + /** + * Determines if an error is a too many requests error (429) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a too many requests error + */ + public static isTooManyRequestsError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_TOO_MANY_REQUESTS; + } + + /** + * Decorates an elastic search cannot execute script error (flavor of 400) + * @public + * @param error the error to decorate + * @param reason the reason for the cannot execute error (optional) + * @returns the decorated error + */ + public static decorateEsCannotExecuteScriptError(error: Error, reason?: string) { + return decorate(error, CODE_ES_CANNOT_EXECUTE_SCRIPT, 400, reason); + } + + /** + * Determines if an error is an elastic search cannot execute script error (flavor of 400) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a cannot execute error + */ + public static isEsCannotExecuteScriptError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_ES_CANNOT_EXECUTE_SCRIPT; + } + + /** + * Decorates an elastic search unavailable error (503) + * @public + * @param error the error to decorate + * @param reason the reason for the elastic search unavailable error (optional) + * @returns the decorated error + */ + public static decorateEsUnavailableError(error: Error, reason?: string) { + return decorate(error, CODE_ES_UNAVAILABLE, 503, reason); + } + + /** + * Determines if an error is an elastic search unavailable error (flavor of 400) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is an elastic search unavailable error + */ + public static isEsUnavailableError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_ES_UNAVAILABLE; + } + + /** + * Decorates a general error (500) + * @public + * @param error the error to decorate + * @param reason the reason for the error (optional) + * @returns the decorated error + */ + public static decorateGeneralError(error: Error, reason?: string) { + return decorate(error, CODE_GENERAL_ERROR, 500, reason); + } + + /** + * Determines if an error is a general error (500) + * @public + * @param error the error or decorated error + * @returns boolean - true if error is a general error + */ + public static isGeneralError(error: Error | DecoratedError) { + return isSavedObjectsClientError(error) && error[code] === CODE_GENERAL_ERROR; + } + + /** + * Creates a generic elastic search not present error + * @public + * @param type the saved object type or null, default null + * @param id the saved object id or null, default null + * @returns the decorated error + */ + public static createGenericNotFoundEsUnavailableError( + // type and id not available in all operations (e.g. mget) + type: string | null = null, + id: string | null = null + ) { + const notFoundError = this.createGenericNotFoundError(type, id); + return this.decorateEsUnavailableError( + new Error(`${notFoundError.message}`), + `x-elastic-product not present or not recognized` + ); + } +} diff --git a/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_management.ts b/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_management.ts index b68fa59d95302..5d72112cbb049 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_management.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/saved_objects_management.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import type { SavedObject } from '..'; import type { SavedObjectsExportTransform } from './export'; import type { SavedObjectsImportHook } from './import'; -import type { SavedObject } from '..'; /** * Configuration options for the {@link SavedObjectsType | type}'s management section. diff --git a/packages/core/saved-objects/core-saved-objects-server/tsconfig.json b/packages/core/saved-objects/core-saved-objects-server/tsconfig.json index 427b46dc70afb..fffc0dc1df6df 100644 --- a/packages/core/saved-objects/core-saved-objects-server/tsconfig.json +++ b/packages/core/saved-objects/core-saved-objects-server/tsconfig.json @@ -18,7 +18,6 @@ "@kbn/core-elasticsearch-server", "@kbn/core-saved-objects-common", "@kbn/core-saved-objects-api-server", - "@kbn/ecs" ], "exclude": [ "target/**/*", diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/index.ts b/packages/core/saved-objects/core-saved-objects-utils-server/index.ts index bae7583ae1e92..102a0a0d38e80 100644 --- a/packages/core/saved-objects/core-saved-objects-utils-server/index.ts +++ b/packages/core/saved-objects/core-saved-objects-utils-server/index.ts @@ -7,7 +7,6 @@ */ export { mergeSavedObjectMigrationMaps } from './src/merge_migration_maps'; -export { SavedObjectsErrorHelpers, type DecoratedError } from './src/saved_objects_error_helpers'; export { SavedObjectsUtils, ALL_NAMESPACES_STRING, diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts deleted file mode 100644 index 7412e744f19e7..0000000000000 --- a/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts +++ /dev/null @@ -1,217 +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. - */ - -import Boom from '@hapi/boom'; - -// 400 - badRequest -const CODE_BAD_REQUEST = 'SavedObjectsClient/badRequest'; -// 400 - invalid version -const CODE_INVALID_VERSION = 'SavedObjectsClient/invalidVersion'; -// 401 - Not Authorized -const CODE_NOT_AUTHORIZED = 'SavedObjectsClient/notAuthorized'; -// 403 - Forbidden -const CODE_FORBIDDEN = 'SavedObjectsClient/forbidden'; -// 413 - Request Entity Too Large -const CODE_REQUEST_ENTITY_TOO_LARGE = 'SavedObjectsClient/requestEntityTooLarge'; -// 404 - Not Found -const CODE_NOT_FOUND = 'SavedObjectsClient/notFound'; -// 409 - Conflict -const CODE_CONFLICT = 'SavedObjectsClient/conflict'; -// 429 - Too Many Requests -const CODE_TOO_MANY_REQUESTS = 'SavedObjectsClient/tooManyRequests'; -// 400 - Es Cannot Execute Script -const CODE_ES_CANNOT_EXECUTE_SCRIPT = 'SavedObjectsClient/esCannotExecuteScript'; -// 503 - Es Unavailable -const CODE_ES_UNAVAILABLE = 'SavedObjectsClient/esUnavailable'; -// 500 - General Error -const CODE_GENERAL_ERROR = 'SavedObjectsClient/generalError'; - -const code = Symbol('SavedObjectsClientErrorCode'); - -export interface DecoratedError extends Boom.Boom { - [code]?: string; -} - -function decorate( - error: Error | DecoratedError, - errorCode: string, - statusCode: number, - message?: string -): DecoratedError { - if (isSavedObjectsClientError(error)) { - return error; - } - - const boom = Boom.boomify(error, { - statusCode, - message, - override: false, - }) as DecoratedError; - - boom[code] = errorCode; - - return boom; -} - -function isSavedObjectsClientError(error: any): error is DecoratedError { - return Boolean(error && error[code]); -} - -function decorateBadRequestError(error: Error, reason?: string) { - return decorate(error, CODE_BAD_REQUEST, 400, reason); -} - -/** - * @public - */ -export class SavedObjectsErrorHelpers { - public static isSavedObjectsClientError(error: any): error is DecoratedError { - return isSavedObjectsClientError(error); - } - - public static decorateBadRequestError(error: Error, reason?: string) { - return decorateBadRequestError(error, reason); - } - - public static createBadRequestError(reason?: string) { - return decorateBadRequestError(new Error('Bad Request'), reason); - } - - public static createUnsupportedTypeError(type: string) { - return decorateBadRequestError( - new Error('Bad Request'), - `Unsupported saved object type: '${type}'` - ); - } - - public static isBadRequestError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_BAD_REQUEST; - } - - public static createInvalidVersionError(versionInput?: string) { - return decorate( - Boom.badRequest(`Invalid version [${versionInput}]`), - CODE_INVALID_VERSION, - 400 - ); - } - - public static isInvalidVersionError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_INVALID_VERSION; - } - - public static decorateNotAuthorizedError(error: Error, reason?: string) { - return decorate(error, CODE_NOT_AUTHORIZED, 401, reason); - } - - public static isNotAuthorizedError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_NOT_AUTHORIZED; - } - - public static decorateForbiddenError(error: Error, reason?: string) { - return decorate(error, CODE_FORBIDDEN, 403, reason); - } - - public static isForbiddenError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_FORBIDDEN; - } - - public static decorateRequestEntityTooLargeError(error: Error, reason?: string) { - return decorate(error, CODE_REQUEST_ENTITY_TOO_LARGE, 413, reason); - } - public static isRequestEntityTooLargeError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_REQUEST_ENTITY_TOO_LARGE; - } - - public static createGenericNotFoundError(type: string | null = null, id: string | null = null) { - if (type && id) { - return decorate(Boom.notFound(`Saved object [${type}/${id}] not found`), CODE_NOT_FOUND, 404); - } - return decorate(Boom.notFound(), CODE_NOT_FOUND, 404); - } - - public static createIndexAliasNotFoundError(alias: string) { - return SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError(Boom.internal(), alias); - } - - public static decorateIndexAliasNotFoundError(error: Error, alias: string) { - return decorate( - error, - CODE_GENERAL_ERROR, - 500, - `Saved object index alias [${alias}] not found` - ); - } - - public static isNotFoundError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_NOT_FOUND; - } - - public static decorateConflictError(error: Error, reason?: string) { - return decorate(error, CODE_CONFLICT, 409, reason); - } - - public static createConflictError(type: string, id: string, reason?: string) { - return SavedObjectsErrorHelpers.decorateConflictError( - Boom.conflict(`Saved object [${type}/${id}] conflict`), - reason - ); - } - - public static isConflictError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_CONFLICT; - } - - public static decorateTooManyRequestsError(error: Error, reason?: string) { - return decorate(error, CODE_TOO_MANY_REQUESTS, 429, reason); - } - - public static createTooManyRequestsError(type: string, id: string) { - return SavedObjectsErrorHelpers.decorateTooManyRequestsError(Boom.tooManyRequests()); - } - - public static isTooManyRequestsError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_TOO_MANY_REQUESTS; - } - - public static decorateEsCannotExecuteScriptError(error: Error, reason?: string) { - return decorate(error, CODE_ES_CANNOT_EXECUTE_SCRIPT, 400, reason); - } - - public static isEsCannotExecuteScriptError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_ES_CANNOT_EXECUTE_SCRIPT; - } - - public static decorateEsUnavailableError(error: Error, reason?: string) { - return decorate(error, CODE_ES_UNAVAILABLE, 503, reason); - } - - public static isEsUnavailableError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_ES_UNAVAILABLE; - } - - public static decorateGeneralError(error: Error, reason?: string) { - return decorate(error, CODE_GENERAL_ERROR, 500, reason); - } - - public static isGeneralError(error: Error | DecoratedError) { - return isSavedObjectsClientError(error) && error[code] === CODE_GENERAL_ERROR; - } - - public static createGenericNotFoundEsUnavailableError( - // type and id not available in all operations (e.g. mget) - type: string | null = null, - id: string | null = null - ) { - const notFoundError = this.createGenericNotFoundError(type, id); - return this.decorateEsUnavailableError( - new Error(`${notFoundError.message}`), - `x-elastic-product not present or not recognized` - ); - } -} diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/clients/ui_settings_client_common.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/clients/ui_settings_client_common.ts index 1f66d05dc0a4c..992a851db99fd 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/clients/ui_settings_client_common.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/clients/ui_settings_client_common.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { createOrUpgradeSavedConfig } from '../create_or_upgrade_saved_config'; import { CannotOverrideError } from '../ui_settings_errors'; import { Cache } from '../cache'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts index 606c6d98538af..63a80e673a487 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts @@ -10,7 +10,7 @@ import { mockTransform, mockGetUpgradeableConfig, } from './create_or_upgrade_saved_config.test.mock'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts index b6532b1c95348..d6e0fffa05868 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts @@ -11,7 +11,7 @@ import { defaults } from 'lodash'; import type { Logger, LogMeta } from '@kbn/logging'; import { asyncForEach } from '@kbn/std'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { getUpgradeableConfig } from './get_upgradeable_config'; import { transforms } from '../saved_objects'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/delete.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/delete.ts index b9aed6153e796..46cbd4d9a7dde 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/delete.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/delete.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server'; import type { InternalUiSettingsRouter } from '../internal_types'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/get.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/get.ts index 11a45ee2f87c9..58afe0ecd8b1a 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/get.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/get.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server'; import { InternalUiSettingsRequestHandlerContext } from '../internal_types'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set.ts index 9f13f86946074..b1e12b160b6a2 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set.ts @@ -8,7 +8,7 @@ import { schema, ValidationError } from '@kbn/config-schema'; import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import type { InternalUiSettingsRequestHandlerContext, diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set_many.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set_many.ts index 71e94ac039304..210e69fb9884f 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set_many.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/routes/set_many.ts @@ -7,7 +7,7 @@ */ import { schema, ValidationError } from '@kbn/config-schema'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { KibanaRequest, KibanaResponseFactory } from '@kbn/core-http-server'; import { IUiSettingsClient } from '@kbn/core-ui-settings-server'; import type { InternalUiSettingsRouter } from '../internal_types'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.test.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.test.ts index 8f3382d4dadd2..3539a71c11bcd 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.test.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.test.ts @@ -7,7 +7,7 @@ */ import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { SavedObject } from '@kbn/core-saved-objects-common'; import type { UpgradeableConfigAttributes } from '../create_or_upgrade_saved_config'; import { transformDefaultIndex } from './transforms'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.ts b/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.ts index 05bfbbf9d3a12..deff89826f018 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.ts +++ b/packages/core/ui-settings/core-ui-settings-server-internal/src/saved_objects/transforms.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import type { UpgradeableConfigAttributes } from '../create_or_upgrade_saved_config'; diff --git a/packages/core/ui-settings/core-ui-settings-server-internal/tsconfig.json b/packages/core/ui-settings/core-ui-settings-server-internal/tsconfig.json index b90718d624e97..8786943d0f6da 100644 --- a/packages/core/ui-settings/core-ui-settings-server-internal/tsconfig.json +++ b/packages/core/ui-settings/core-ui-settings-server-internal/tsconfig.json @@ -22,7 +22,6 @@ "@kbn/core-saved-objects-api-server", "@kbn/core-saved-objects-server", "@kbn/core-saved-objects-server-internal", - "@kbn/core-saved-objects-utils-server", "@kbn/core-ui-settings-common", "@kbn/core-ui-settings-server", "@kbn/config", diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 3ed069403d8f3..6179f1f534395 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -255,6 +255,7 @@ export type { SavedObjectsImportWarning, SavedObjectTypeIdTuple, } from '@kbn/core-saved-objects-common'; + export type { SavedObjectsBulkCreateObject, SavedObjectsBulkGetObject, @@ -358,12 +359,9 @@ export type { SavedObjectsRequestHandlerContext, EncryptedObjectDescriptor, ISavedObjectsEncryptionExtension, - PerformAuthorizationParams, AuthorizationTypeEntry, AuthorizationTypeMap, CheckAuthorizationResult, - EnforceAuthorizationParams, - AddAuditEventParams, RedactNamespacesParams, ISavedObjectsSecurityExtension, ISavedObjectsSpacesExtension, @@ -373,9 +371,9 @@ export { ENCRYPTION_EXTENSION_ID, SECURITY_EXTENSION_ID, SPACES_EXTENSION_ID, + SavedObjectsErrorHelpers, } from '@kbn/core-saved-objects-server'; export { - SavedObjectsErrorHelpers, SavedObjectsUtils, mergeSavedObjectMigrationMaps, } from '@kbn/core-saved-objects-utils-server'; diff --git a/src/core/server/integration_tests/saved_objects/routes/import.test.ts b/src/core/server/integration_tests/saved_objects/routes/import.test.ts index 6a98122141b00..c7fbbc8c81325 100644 --- a/src/core/server/integration_tests/saved_objects/routes/import.test.ts +++ b/src/core/server/integration_tests/saved_objects/routes/import.test.ts @@ -9,7 +9,7 @@ jest.mock('uuid'); import supertest from 'supertest'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-internal'; import { diff --git a/src/plugins/telemetry/server/saved_objects/get_telemetry_saved_object.ts b/src/plugins/telemetry/server/saved_objects/get_telemetry_saved_object.ts index 5a1509a028f90..b12d9e9383f64 100644 --- a/src/plugins/telemetry/server/saved_objects/get_telemetry_saved_object.ts +++ b/src/plugins/telemetry/server/saved_objects/get_telemetry_saved_object.ts @@ -7,7 +7,7 @@ */ import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { TelemetrySavedObject } from './types'; import { TELEMETRY_SAVED_OBJECT_TYPE, TELEMETRY_SAVED_OBJECT_ID } from './constants'; diff --git a/src/plugins/telemetry/tsconfig.json b/src/plugins/telemetry/tsconfig.json index adb252c1665e2..4198aa6a3b0e1 100644 --- a/src/plugins/telemetry/tsconfig.json +++ b/src/plugins/telemetry/tsconfig.json @@ -31,7 +31,6 @@ "@kbn/utils", "@kbn/core-saved-objects-server", "@kbn/core-saved-objects-api-server", - "@kbn/core-saved-objects-utils-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/fleet/server/services/epm/archive/storage.test.ts b/x-pack/plugins/fleet/server/services/epm/archive/storage.test.ts index 10ee590e3e398..1bfa783d3a59a 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/storage.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/storage.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { getAsset } from './storage'; diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 7c803170c333d..3cd28d6cb1f93 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -89,7 +89,6 @@ "@kbn/core-saved-objects-api-server", "@kbn/logging", "@kbn/analytics-client", - "@kbn/core-saved-objects-utils-server", "@kbn/core-logging-server-mocks", "@kbn/ml-is-populated-object", "@kbn/utils", diff --git a/x-pack/plugins/observability/server/services/slo/slo_repository.ts b/x-pack/plugins/observability/server/services/slo/slo_repository.ts index ac639bd383a2f..3bf0c3b85937b 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_repository.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_repository.ts @@ -10,7 +10,7 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { sloSchema } from '@kbn/slo-schema'; import { StoredSLO, SLO } from '../../domain/models'; diff --git a/x-pack/plugins/observability/tsconfig.json b/x-pack/plugins/observability/tsconfig.json index d08a00ea674a3..1ee05fa174fae 100644 --- a/x-pack/plugins/observability/tsconfig.json +++ b/x-pack/plugins/observability/tsconfig.json @@ -62,7 +62,6 @@ "@kbn/logging-mocks", "@kbn/logging", "@kbn/core-saved-objects-api-server", - "@kbn/core-saved-objects-utils-server", "@kbn/share-plugin", "@kbn/core-notifications-browser", "@kbn/slo-schema", diff --git a/x-pack/plugins/security/server/audit/audit_events.test.ts b/x-pack/plugins/security/server/audit/audit_events.test.ts index 8edd22e915c7c..b78a723fe3f4e 100644 --- a/x-pack/plugins/security/server/audit/audit_events.test.ts +++ b/x-pack/plugins/security/server/audit/audit_events.test.ts @@ -7,11 +7,11 @@ import { URL } from 'url'; -import { AuditAction } from '@kbn/core-saved-objects-server'; import { httpServerMock } from '@kbn/core/server/mocks'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; import { AuthenticationResult } from '../authentication'; +import { AuditAction } from '../saved_objects/saved_objects_security_extension'; import { httpRequestEvent, savedObjectEvent, diff --git a/x-pack/plugins/security/server/audit/audit_events.ts b/x-pack/plugins/security/server/audit/audit_events.ts index 42cc7d4712af2..30763e7dd71ec 100644 --- a/x-pack/plugins/security/server/audit/audit_events.ts +++ b/x-pack/plugins/security/server/audit/audit_events.ts @@ -5,15 +5,15 @@ * 2.0. */ -import type { - AuditAction, - AddAuditEventParams as SavedObjectEventParams, -} from '@kbn/core-saved-objects-server'; import type { EcsEvent, KibanaRequest, LogMeta } from '@kbn/core/server'; import type { ArrayElement } from '@kbn/utility-types'; import type { AuthenticationProvider } from '../../common/model'; import type { AuthenticationResult } from '../authentication/authentication_result'; +import type { + AuditAction, + AddAuditEventParams as SavedObjectEventParams, +} from '../saved_objects/saved_objects_security_extension'; /** * Audit kibana schema using ECS format diff --git a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts index bd6eb1fa1456e..88de1b783ae65 100644 --- a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts +++ b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.test.ts @@ -5,14 +5,95 @@ * 2.0. */ -import { AuditAction } from '@kbn/core-saved-objects-server'; -import type { EcsEvent, SavedObjectsClient } from '@kbn/core/server'; +import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; +import type { + AuthorizeBulkGetObject, + AuthorizeCreateObject, + AuthorizeObjectWithExistingSpaces, + AuthorizeUpdateObject, + BulkResolveError, +} from '@kbn/core-saved-objects-server'; +import type { + SavedObjectReferenceWithContext, + SavedObjectsClient, + SavedObjectsFindResult, + SavedObjectsResolveResponse, +} from '@kbn/core/server'; import { auditLoggerMock } from '../audit/mocks'; import type { CheckSavedObjectsPrivileges } from '../authorization'; import { Actions } from '../authorization'; import type { CheckPrivilegesResponse } from '../authorization/types'; -import { SavedObjectsSecurityExtension } from './saved_objects_security_extension'; +import { + AuditAction, + SavedObjectsSecurityExtension, + SecurityAction, +} from './saved_objects_security_extension'; + +const checkAuthorizationSpy = jest.spyOn( + SavedObjectsSecurityExtension.prototype as any, + 'checkAuthorization' +); +const enforceAuthorizationSpy = jest.spyOn( + SavedObjectsSecurityExtension.prototype as any, + 'enforceAuthorization' +); +const redactNamespacesSpy = jest.spyOn( + SavedObjectsSecurityExtension.prototype as any, + 'redactNamespaces' +); +const authorizeSpy = jest.spyOn(SavedObjectsSecurityExtension.prototype as any, 'authorize'); +const auditHelperSpy = jest.spyOn(SavedObjectsSecurityExtension.prototype as any, 'auditHelper'); +const addAuditEventSpy = jest.spyOn( + SavedObjectsSecurityExtension.prototype as any, + 'addAuditEvent' +); + +const obj1 = { + type: 'a', + id: '6.0.0-alpha1', + objectNamespace: 'foo', + initialNamespaces: ['foo'], + existingNamespaces: [], +}; +const obj2 = { + type: 'b', + id: 'logstash-*', + objectNamespace: undefined, + initialNamespaces: undefined, + existingNamespaces: [], +}; +const obj3 = { + type: 'c', + id: '6.0.0-charlie3', + objectNamespace: undefined, + initialNamespaces: undefined, + existingNamespaces: ['bar'], +}; +const obj4 = { + type: 'd', + id: '6.0.0-disco4', + objectNamespace: 'y', + initialNamespaces: ['y'], + existingNamespaces: ['z'], +}; + +function setupSimpleCheckPrivsMockResolve( + checkPrivileges: jest.MockedFunction, + type: string, + action: string, + authorized: boolean +) { + checkPrivileges.mockResolvedValue({ + hasAllRequested: authorized, + privileges: { + kibana: [ + { privilege: `mock-saved_object:${type}/${action}`, authorized }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); +} function setup() { const actions = new Actions('some-version'); @@ -34,161 +115,80 @@ function setup() { return { actions, auditLogger, errors, checkPrivileges, securityExtension }; } -describe('#enforceAuthorization', () => { - test('fully authorized', () => { - const { securityExtension } = setup(); - - const authorizationResult = { - status: 'fully_authorized', - typeMap: new Map() - .set('a', { - foo: { isGloballyAuthorized: true, authorizedSpaces: [] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }) - .set('b', { - foo: { authorizedSpaces: ['x', 'y'] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }) - .set('c', { - foo: { authorizedSpaces: ['y', 'z'] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }), - }; - - const spacesToEnforce = new Set(['x', 'y', 'z']); - - expect(() => - securityExtension.enforceAuthorization({ - typesAndSpaces: new Map([ - ['a', spacesToEnforce], - ['b', new Set(['x', 'y'])], - ['c', new Set(['y', 'z'])], - ]), - action: 'foo', - typeMap: authorizationResult.typeMap, - }) - ).not.toThrowError(); - }); - - test('partially authorized', () => { - const { securityExtension } = setup(); - - const authorizationResult = { - status: 'partially_authorized', - typeMap: new Map() - .set('a', { - foo: { isGloballyAuthorized: true, authorizedSpaces: [] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }) - .set('b', { - foo: { authorizedSpaces: ['x'] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }) - .set('c', { - foo: { authorizedSpaces: ['z'] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }), - }; - - const spacesToEnforce = new Set(['x', 'y', 'z']); - - expect(() => - securityExtension.enforceAuthorization({ - typesAndSpaces: new Map([ - ['a', spacesToEnforce], - ['b', new Set(['x', 'y'])], - ['c', new Set(['y', 'z'])], - ]), - action: 'foo', - typeMap: authorizationResult.typeMap, - }) - ).toThrowError('Unable to foo b,c'); +describe('#authorize (unpublished by interface)', () => { + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); }); - test('unauthorized', () => { - const { securityExtension } = setup(); - - const authorizationResult = { - status: 'unauthorized', - typeMap: new Map() - .set('a', { - foo: { authorizedSpaces: ['x'] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }) - .set('b', { - foo: { authorizedSpaces: ['y'] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }) - .set('c', { - foo: { authorizedSpaces: ['z'] }, - ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, - }), - }; - - expect(() => - securityExtension.enforceAuthorization({ - typesAndSpaces: new Map([ - ['a', new Set(['y', 'z'])], - ['b', new Set(['x', 'z'])], - ['c', new Set(['x', 'y'])], - ]), - action: 'foo', - typeMap: authorizationResult.typeMap, - }) - ).toThrowError('Unable to foo a,b,c'); - }); -}); + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, + { privilege: 'mock-saved_object:a/create', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:c/create', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; -describe('#performAuthorization', () => { describe('without enforce', () => { // These arguments are used for all unit tests below const types = new Set(['a', 'b', 'c']); const spaces = new Set(['x', 'y']); - const actions = new Set(['foo', 'bar']); + const actions = new Set([SecurityAction.BULK_UPDATE, SecurityAction.CREATE]); - const fullyAuthorizedCheckPrivilegesResponse = { - hasAllRequested: true, - privileges: { - kibana: [ - { privilege: 'mock-saved_object:a/foo', authorized: true }, - { privilege: 'mock-saved_object:a/bar', authorized: true }, - { privilege: 'login:', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:b/bar', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:c/foo', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:c/bar', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:b/bar', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:c/foo', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:c/bar', authorized: true }, - ], - }, - } as CheckPrivilegesResponse; - test('calls performPrivileges with expected privilege actions and namespaces', async () => { + test('checks authorization with expected actions, types, and spaces', async () => { const { securityExtension, checkPrivileges } = setup(); - checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); - await securityExtension.performAuthorization({ types, spaces, actions }); + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ types, spaces, actions }); + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set(['bulk_update', 'create']), + spaces, + types, + options: { allowGlobalResource: false }, + }); + expect(checkPrivileges).toHaveBeenCalledTimes(1); expect(checkPrivileges).toHaveBeenCalledWith( [ - 'mock-saved_object:a/foo', - 'mock-saved_object:a/bar', - 'mock-saved_object:b/foo', - 'mock-saved_object:b/bar', - 'mock-saved_object:c/foo', - 'mock-saved_object:c/bar', + 'mock-saved_object:a/bulk_update', + 'mock-saved_object:a/create', + 'mock-saved_object:b/bulk_update', + 'mock-saved_object:b/create', + 'mock-saved_object:c/bulk_update', + 'mock-saved_object:c/create', 'login:', ], [...spaces] ); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); }); test('throws an error when `types` is empty', async () => { const { securityExtension, checkPrivileges } = setup(); await expect( - securityExtension.performAuthorization({ types: new Set(), spaces, actions }) - ).rejects.toThrowError('No types specified for authorization check'); + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ types: new Set(), spaces, actions }) + ).rejects.toThrowError('No types specified for authorization'); + expect(checkAuthorizationSpy).not.toHaveBeenCalled(); expect(checkPrivileges).not.toHaveBeenCalled(); }); @@ -196,8 +196,10 @@ describe('#performAuthorization', () => { const { securityExtension, checkPrivileges } = setup(); await expect( - securityExtension.performAuthorization({ types, spaces: new Set(), actions }) - ).rejects.toThrowError('No spaces specified for authorization check'); + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ types, spaces: new Set(), actions }) + ).rejects.toThrowError('No spaces specified for authorization'); expect(checkPrivileges).not.toHaveBeenCalled(); }); @@ -205,8 +207,10 @@ describe('#performAuthorization', () => { const { securityExtension, checkPrivileges } = setup(); await expect( - securityExtension.performAuthorization({ types, spaces, actions: new Set([]) }) - ).rejects.toThrowError('No actions specified for authorization check'); + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ types, spaces, actions: new Set() }) + ).rejects.toThrowError('No actions specified for authorization'); expect(checkPrivileges).not.toHaveBeenCalled(); }); @@ -215,64 +219,70 @@ describe('#performAuthorization', () => { checkPrivileges.mockRejectedValue(new Error('Oh no!')); await expect( - securityExtension.performAuthorization({ types, spaces, actions }) + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ types, spaces, actions }) ).rejects.toThrowError('Oh no!'); }); test('fully authorized', async () => { - const { securityExtension, checkPrivileges } = setup(); + const { securityExtension, checkPrivileges, auditLogger } = setup(); checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); - const result = await securityExtension.performAuthorization({ types, spaces, actions }); - + // Disable to test method + // eslint-disable-next-line dot-notation + const result = await securityExtension['authorize']({ types, spaces, actions }); + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + expect(auditLogger.log).not.toHaveBeenCalled(); // We're performing authz but no enforce, therefore no audit expect(result).toEqual({ status: 'fully_authorized', typeMap: new Map() .set('a', { - foo: { isGloballyAuthorized: true, authorizedSpaces: [] }, - bar: { isGloballyAuthorized: true, authorizedSpaces: [] }, + create: { isGloballyAuthorized: true, authorizedSpaces: [] }, + bulk_update: { isGloballyAuthorized: true, authorizedSpaces: [] }, // Technically, 'login:' is not a saved object action, it is a Kibana privilege -- however, we include it in the `typeMap` results // for ease of use with the `redactNamespaces` function. The user is never actually authorized to "login" for a given object type, // they are authorized to log in on a per-space basis, and this is applied to each object type in the typeMap result accordingly. ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, }) .set('b', { - foo: { authorizedSpaces: ['x', 'y'] }, - bar: { authorizedSpaces: ['x', 'y'] }, + create: { authorizedSpaces: ['x', 'y'] }, + bulk_update: { authorizedSpaces: ['x', 'y'] }, ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, }) .set('c', { - foo: { authorizedSpaces: ['x', 'y'] }, - bar: { authorizedSpaces: ['x', 'y'] }, + create: { authorizedSpaces: ['x', 'y'] }, + bulk_update: { authorizedSpaces: ['x', 'y'] }, ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, }), }); }); test('partially authorized', async () => { - const { securityExtension, checkPrivileges } = setup(); + const { securityExtension, checkPrivileges, auditLogger } = setup(); checkPrivileges.mockResolvedValue({ hasAllRequested: false, privileges: { kibana: [ - // For type 'a', the user is authorized to use 'foo' action but not 'bar' action (all spaces) - // For type 'b', the user is authorized to use 'foo' action but not 'bar' action (both spaces) + // For type 'a', the user is authorized to use 'create' action but not 'update' action (all spaces) + // For type 'b', the user is authorized to use 'create' action but not 'update' action (both spaces) // For type 'c', the user is authorized to use both actions in space 'x' but not space 'y' - { privilege: 'mock-saved_object:a/foo', authorized: true }, - { privilege: 'mock-saved_object:a/bar', authorized: false }, - { privilege: 'mock-saved_object:a/bar', authorized: true }, // fail-secure check - { resource: 'x', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:b/bar', authorized: false }, - { resource: 'x', privilege: 'mock-saved_object:c/foo', authorized: true }, - { privilege: 'mock-saved_object:c/foo', authorized: false }, // inverse fail-secure check - { resource: 'x', privilege: 'mock-saved_object:c/bar', authorized: true }, + { privilege: 'mock-saved_object:a/create', authorized: true }, + { privilege: 'mock-saved_object:a/bulk_update', authorized: false }, + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, // fail-secure check + { resource: 'x', privilege: 'mock-saved_object:b/create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_update', authorized: false }, + { resource: 'x', privilege: 'mock-saved_object:c/create', authorized: true }, + { privilege: 'mock-saved_object:c/create', authorized: false }, // inverse fail-secure check + { resource: 'x', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, { resource: 'x', privilege: 'login:', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:b/bar', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:c/foo', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:c/bar', authorized: false }, - { privilege: 'mock-saved_object:c/bar', authorized: true }, // fail-secure check - { resource: 'y', privilege: 'mock-saved_object:c/bar', authorized: true }, // fail-secure check + { resource: 'y', privilege: 'mock-saved_object:b/create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_update', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:c/create', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:c/bulk_update', authorized: false }, + { privilege: 'mock-saved_object:c/bulk_update', authorized: true }, // fail-secure check + { resource: 'y', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, // fail-secure check { resource: 'y', privilege: 'login:', authorized: true }, // The fail-secure checks are a contrived scenario, as we *shouldn't* get both an unauthorized and authorized result for a given resource... // However, in case we do, we should fail-secure (authorized + unauthorized = unauthorized) @@ -280,49 +290,54 @@ describe('#performAuthorization', () => { }, } as CheckPrivilegesResponse); - const result = await securityExtension.performAuthorization({ types, spaces, actions }); + // Disable to test method + // eslint-disable-next-line dot-notation + const result = await securityExtension['authorize']({ types, spaces, actions }); + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + expect(auditLogger.log).not.toHaveBeenCalled(); // We're performing authz but no enforce, therefore no audit expect(result).toEqual({ status: 'partially_authorized', typeMap: new Map() .set('a', { - foo: { isGloballyAuthorized: true, authorizedSpaces: [] }, + create: { isGloballyAuthorized: true, authorizedSpaces: [] }, ['login:']: { authorizedSpaces: ['x', 'y'] }, }) .set('b', { - foo: { authorizedSpaces: ['x', 'y'] }, + create: { authorizedSpaces: ['x', 'y'] }, ['login:']: { authorizedSpaces: ['x', 'y'] }, }) .set('c', { - foo: { authorizedSpaces: ['x'] }, - bar: { authorizedSpaces: ['x'] }, + bulk_update: { authorizedSpaces: ['x'] }, + create: { authorizedSpaces: ['x'] }, ['login:']: { authorizedSpaces: ['x', 'y'] }, }), }); }); test('unauthorized', async () => { - const { securityExtension, checkPrivileges } = setup(); + const { securityExtension, checkPrivileges, auditLogger } = setup(); checkPrivileges.mockResolvedValue({ hasAllRequested: false, privileges: { kibana: [ - { privilege: 'mock-saved_object:a/foo', authorized: false }, - { privilege: 'mock-saved_object:a/bar', authorized: false }, - { privilege: 'mock-saved_object:a/bar', authorized: true }, // fail-secure check - { resource: 'x', privilege: 'mock-saved_object:b/foo', authorized: false }, - { resource: 'x', privilege: 'mock-saved_object:b/bar', authorized: false }, - { resource: 'x', privilege: 'mock-saved_object:c/foo', authorized: false }, - { resource: 'x', privilege: 'mock-saved_object:c/bar', authorized: false }, + { privilege: 'mock-saved_object:a/create', authorized: false }, + { privilege: 'mock-saved_object:a/update', authorized: false }, + { privilege: 'mock-saved_object:a/update', authorized: true }, // fail-secure check + { resource: 'x', privilege: 'mock-saved_object:b/create', authorized: false }, + { resource: 'x', privilege: 'mock-saved_object:b/update', authorized: false }, + { resource: 'x', privilege: 'mock-saved_object:c/create', authorized: false }, + { resource: 'x', privilege: 'mock-saved_object:c/update', authorized: false }, { resource: 'x', privilege: 'login:', authorized: false }, { resource: 'x', privilege: 'login:', authorized: true }, // fail-secure check - { resource: 'y', privilege: 'mock-saved_object:a/foo', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:a/bar', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:b/foo', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:b/bar', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:c/foo', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:c/bar', authorized: false }, - { privilege: 'mock-saved_object:c/bar', authorized: true }, // fail-secure check - { resource: 'y', privilege: 'mock-saved_object:c/bar', authorized: true }, // fail-secure check + { resource: 'y', privilege: 'mock-saved_object:a/create', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:a/update', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:b/create', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:b/update', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:c/create', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:c/update', authorized: false }, + { privilege: 'mock-saved_object:c/update', authorized: true }, // fail-secure check + { resource: 'y', privilege: 'mock-saved_object:c/update', authorized: true }, // fail-secure check { resource: 'y', privilege: 'login:', authorized: true }, // should *not* result in a 'partially_authorized' status // The fail-secure checks are a contrived scenario, as we *shouldn't* get both an unauthorized and authorized result for a given resource... // However, in case we do, we should fail-secure (authorized + unauthorized = unauthorized) @@ -330,7 +345,12 @@ describe('#performAuthorization', () => { }, } as CheckPrivilegesResponse); - const result = await securityExtension.performAuthorization({ types, spaces, actions }); + // Disable to test method + // eslint-disable-next-line dot-notation + const result = await securityExtension['authorize']({ types, spaces, actions }); + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + expect(auditLogger.log).not.toHaveBeenCalled(); // We're performing authz but no enforce, therefore no audit expect(result).toEqual({ // The user is authorized to log into space Y, but they are not authorized to take any actions on any of the requested object types. // Therefore, the status is 'unauthorized'. @@ -347,10 +367,10 @@ describe('#performAuthorization', () => { hasAllRequested: true, privileges: { kibana: [ - // redundant conflicting privileges for space X, type B, action Foo - { resource: 'x', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:b/foo', authorized: false }, - { resource: 'y', privilege: 'mock-saved_object:b/foo', authorized: true }, + // redundant conflicting privileges for space X, type B, action Create + { resource: 'x', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_update', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, ], }, } as CheckPrivilegesResponse; @@ -358,11 +378,13 @@ describe('#performAuthorization', () => { const { securityExtension, checkPrivileges } = setup(); checkPrivileges.mockResolvedValue(conflictingPrivilegesResponse); - const result = await securityExtension.performAuthorization({ types, spaces, actions }); + // Disable to test method + // eslint-disable-next-line dot-notation + const result = await securityExtension['authorize']({ types, spaces, actions }); expect(result).toEqual({ status: 'fully_authorized', typeMap: new Map().set('b', { - foo: { authorizedSpaces: ['y'] }, // should NOT be authorized for conflicted privilege + bulk_update: { authorizedSpaces: ['y'] }, // should NOT be authorized for conflicted privilege }), }); }); @@ -372,30 +394,16 @@ describe('#performAuthorization', () => { // These arguments are used for all unit tests below const types = new Set(['a', 'b', 'c']); const spaces = new Set(['x', 'y']); - const actions = new Set(['foo']); - - const fullyAuthorizedCheckPrivilegesResponse = { - hasAllRequested: true, - privileges: { - kibana: [ - { privilege: 'mock-saved_object:a/foo', authorized: true }, - { privilege: 'login:', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:c/foo', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:c/foo', authorized: true }, - ], - }, - } as CheckPrivilegesResponse; + const actions = new Set([SecurityAction.BULK_UPDATE, SecurityAction.CREATE]); const partiallyAuthorizedCheckPrivilegesResponse = { hasAllRequested: false, privileges: { kibana: [ - { privilege: 'mock-saved_object:a/foo', authorized: true }, + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, { privilege: 'login:', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:c/foo', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, ], }, } as CheckPrivilegesResponse; @@ -405,19 +413,21 @@ describe('#performAuthorization', () => { privileges: { kibana: [ { privilege: 'login:', authorized: true }, - { resource: 'x', privilege: 'mock-saved_object:a/foo', authorized: true }, - { resource: 'y', privilege: 'mock-saved_object:b/foo', authorized: true }, - { resource: 'z', privilege: 'mock-saved_object:c/foo', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:a/bulk_update', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, ], }, } as CheckPrivilegesResponse; - test('fully authorized', async () => { - const { securityExtension, checkPrivileges } = setup(); - checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + describe(`fully authorized`, () => { + test('adds default audit event', async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); - await expect(() => - securityExtension.performAuthorization({ + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ actions, types, spaces, @@ -426,198 +436,5653 @@ describe('#performAuthorization', () => { ['b', new Set(['x', 'y'])], ['c', new Set(['y'])], ]), - }) - ).not.toThrowError(); - }); + }); - test('partially authorized', async () => { - const { securityExtension, checkPrivileges } = setup(); - checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); + expect(auditLogger.log).toHaveBeenCalledTimes(2); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'User is updating saved objects', + }); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'unknown', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'User is creating saved objects', + }); + }); + + test(`adds audit event with success outcome when 'useSuccessOutcome' is true`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); - await expect(() => - securityExtension.performAuthorization({ + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ actions, types, spaces, enforceMap: new Map([ ['a', new Set(['x', 'y', 'z'])], ['b', new Set(['x', 'y'])], - ['c', new Set(['x', 'y'])], + ['c', new Set(['y'])], ]), - }) - ).rejects.toThrowError('Unable to foo b,c'); - }); + auditOptions: { useSuccessOutcome: true }, + }); - test('unauthorized', async () => { - const { securityExtension, checkPrivileges } = setup(); - checkPrivileges.mockResolvedValue(unauthorizedCheckPrivilegesResponse); + expect(auditLogger.log).toHaveBeenCalledTimes(2); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'success', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'User has updated saved objects', + }); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'success', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'User has created saved objects', + }); + }); + + test(`adds audit event per object when 'objects' is populated`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; - await expect(() => - securityExtension.performAuthorization({ + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ actions, types, spaces, enforceMap: new Map([ - ['a', new Set(['y', 'z'])], - ['b', new Set(['x', 'z'])], - ['c', new Set(['x', 'y'])], + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['y'])], ]), - }) - ).rejects.toThrowError('Unable to foo a,b,c'); + auditOptions: { + objects: auditObjects, + }, + }); + + expect(auditLogger.log).toHaveBeenCalledTimes(auditObjects.length * 2); // 2 actions + let i = 1; + for (const obj of auditObjects) { + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { + id: obj.id, + type: obj.type, + }, + }, + message: `User is updating ${obj.type} [id=${obj.id}]`, + }); + } + for (const obj of auditObjects) { + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: undefined, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'unknown', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { + id: obj.id, + type: obj.type, + }, + }, + message: `User is creating ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`adds audit event per object with success outcome when 'objects' is populated and 'useSuccessOutcome' is true`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; + + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['y'])], + ]), + auditOptions: { + objects: auditObjects, + useSuccessOutcome: true, + }, + }); + + expect(auditLogger.log).toHaveBeenCalledTimes(auditObjects.length * 2); // two action + let i = 1; + for (const obj of auditObjects) { + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'success', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { + id: obj.id, + type: obj.type, + }, + }, + message: `User has updated ${obj.type} [id=${obj.id}]`, + }); + } + for (const obj of auditObjects) { + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: undefined, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'success', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { + id: obj.id, + type: obj.type, + }, + }, + message: `User has created ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`does not add audit events when 'bypassOnSuccess' is true`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; + + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['y'])], + ]), + auditOptions: { + objects: auditObjects, + bypass: 'on_success', + }, + }); + + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + + test(`auditOptions.bypassOnFailure' has no effect`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['y'])], + ]), + auditOptions: { bypass: 'on_failure' }, + }); + + expect(auditLogger.log).toHaveBeenCalledTimes(actions.size); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'User is updating saved objects', + }); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'unknown', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'User is creating saved objects', + }); + }); }); - }); -}); -describe('#addAuditEvent', () => { - test(`adds an unknown audit event`, async () => { - const { auditLogger, securityExtension } = setup(); - const action = AuditAction.UPDATE_OBJECTS_SPACES; - const outcome: EcsEvent['outcome'] = 'unknown'; - const savedObject = { type: 'dashboard', id: '3' }; - const spaces = ['space-id']; - - const auditParams = { - action, - outcome, - savedObject, - deleteFromSpaces: spaces, - }; + describe(`partially authorized`, () => { + test('throws error and adds default audit event', async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); - securityExtension.addAuditEvent(auditParams); + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['x', 'y'])], + ]), + }) + ).rejects.toThrowError('Unable to bulk_update b,c'); - expect(auditLogger.log).toHaveBeenCalledWith( - expect.objectContaining({ - event: expect.objectContaining({ - action, - outcome, - }), - kibana: savedObject - ? expect.objectContaining({ - saved_object: savedObject, - delete_from_spaces: spaces, - }) - : expect.anything(), - message: `User is updating spaces of ${savedObject.type} [id=${savedObject.id}]`, - }) - ); - }); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to bulk_update b,c', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'Failed attempt to update saved objects', + }); + }); - test(`adds a success audit event`, async () => { - const { auditLogger, securityExtension } = setup(); - const action = AuditAction.UPDATE_OBJECTS_SPACES; - const outcome: EcsEvent['outcome'] = 'success'; - const savedObject = { type: 'dashboard', id: '3' }; - const spaces = ['space-id']; + test(`throws error and adds audit event per object when 'objects' is populated`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); - const auditParams = { - action, - outcome, - savedObject, - addToSpaces: spaces, - }; + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; - securityExtension.addAuditEvent(auditParams); + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { objects: auditObjects }, + }) + ).rejects.toThrowError('Unable to bulk_update b,c'); - expect(auditLogger.log).toHaveBeenCalledWith( - expect.objectContaining({ - event: expect.objectContaining({ - action, - outcome, - }), - kibana: savedObject - ? expect.objectContaining({ - saved_object: savedObject, - add_to_spaces: spaces, - }) - : expect.anything(), - message: `User has updated spaces of ${savedObject.type} [id=${savedObject.id}]`, - }) - ); - }); + expect(auditLogger.log).toHaveBeenCalledTimes(auditObjects.length); + for (const obj of auditObjects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to bulk_update b,c', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { + id: obj.id, + type: obj.type, + }, + }, + message: `Failed attempt to update ${obj.type} [id=${obj.id}]`, + }); + } + }); - test(`adds a failure audit event`, async () => { - const { auditLogger, securityExtension } = setup(); - const action = AuditAction.DELETE; - const outcome: EcsEvent['outcome'] = 'failure'; - const savedObject = { type: 'dashboard', id: '3' }; - const error: Error = { - name: 'test_error', - message: 'this is just a test', - }; + test(`throws error and does not add an audit event when 'bypassOnFailure' is true`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); - const auditParams = { - action, - outcome, - savedObject, - error, - }; + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; - securityExtension.addAuditEvent(auditParams); + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { objects: auditObjects, bypass: 'on_failure' }, + }) + ).rejects.toThrowError('Unable to bulk_update b,c'); - expect(auditLogger.log).toHaveBeenCalledWith( - expect.objectContaining({ - error: { code: error.name, message: error.message }, - event: expect.objectContaining({ - action, - outcome, - }), - kibana: savedObject - ? expect.objectContaining({ - saved_object: savedObject, - }) - : expect.anything(), - message: `Failed attempt to delete ${savedObject.type} [id=${savedObject.id}]`, - }) - ); - }); -}); + expect(auditLogger.log).not.toHaveBeenCalled(); + }); -describe('#redactNamespaces', () => { - test(`filters namespaces that the user doesn't have access to`, () => { - const { securityExtension } = setup(); + test(`auditOptions.bypassOnSuccess' has no effect`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); - const typeMap = new Map().set('so-type', { - // redact is only concerned with 'login' attribute, not specific action - ['login:']: { authorizedSpaces: ['authorized-space'] }, + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { bypass: 'on_success' }, + }) + ).rejects.toThrowError('Unable to bulk_update b,c'); + + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to bulk_update b,c', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'Failed attempt to update saved objects', + }); + }); }); - const so = { - id: 'some-id', - type: 'so-type', - namespaces: ['authorized-space', 'unauthorized-space'], - attributes: { - test: 'attr', - }, - score: 1, - references: [], - }; + describe(`unauthorized`, () => { + test('throws error and adds default audit event', async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(unauthorizedCheckPrivilegesResponse); - const result = securityExtension.redactNamespaces({ typeMap, savedObject: so }); - expect(result).toEqual(expect.objectContaining({ namespaces: ['authorized-space', '?'] })); + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['y', 'z'])], + ['b', new Set(['x', 'z'])], + ['c', new Set(['x', 'y'])], + ]), + }) + ).rejects.toThrowError('Unable to bulk_update a,b,c'); + + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to bulk_update a,b,c', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'Failed attempt to update saved objects', + }); + }); + + test(`throws error and adds audit event per object when 'objects' is populated`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(unauthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; + + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { objects: auditObjects }, + }) + ).rejects.toThrowError('Unable to bulk_update a,b,c'); + + expect(auditLogger.log).toHaveBeenCalledTimes(auditObjects.length); + for (const obj of auditObjects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to bulk_update a,b,c', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { + id: obj.id, + type: obj.type, + }, + }, + message: `Failed attempt to update ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`throws error and does not add an audit event when 'bypassOnFailure' is true`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(unauthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; + + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { objects: auditObjects, bypass: 'on_failure' }, + }) + ).rejects.toThrowError('Unable to bulk_update a,b,c'); + + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + + test(`auditOptions.bypassOnSuccess' has no effect`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(unauthorizedCheckPrivilegesResponse); + + await expect(() => + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['y', 'z'])], + ['b', new Set(['x', 'z'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { bypass: 'on_success' }, + }) + ).rejects.toThrowError('Unable to bulk_update a,b,c'); + + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to bulk_update a,b,c', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: 'Failed attempt to update saved objects', + }); + }); + }); }); - test(`does not redact on isGloballyAuthorized`, () => { - const { securityExtension } = setup(); + describe('security actions with no authorization action', () => { + // These arguments are used for all unit tests below + const types = new Set(['a', 'b', 'c']); + const spaces = new Set(['x', 'y']); - const typeMap = new Map().set('so-type', { - // redact is only concerned with 'login' attribute, not specific action - ['login:']: { isGloballyAuthorized: true }, + test('throws no actions error', async () => { + const { securityExtension } = setup(); + + await expect( + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + types, + spaces, + actions: new Set([SecurityAction.CLOSE_POINT_IN_TIME]), // this is currently the only security action that does not require authz + }) + ).rejects.toThrowError('No actions specified for authorization check'); }); + }); - const so = { - id: 'some-id', - type: 'so-type', - namespaces: ['space-a', 'space-b', 'space-c'], - attributes: { - test: 'attr', + describe('scecurity actions with no audit action', () => { + // These arguments are used for all unit tests below + const types = new Set(['a', 'b', 'c']); + const spaces = new Set(['x', 'y']); + // Check conflicts is currently the only security action without an audit action + const actions = new Set([SecurityAction.CHECK_CONFLICTS]); + // eslint-disable-next-line @typescript-eslint/no-shadow + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + ], }, - score: 1, - references: [], - }; + } as CheckPrivilegesResponse; - const result = securityExtension.redactNamespaces({ typeMap, savedObject: so }); - expect(result).toEqual( - expect.objectContaining({ namespaces: ['space-a', 'space-b', 'space-c'] }) - ); + const partiallyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const unauthorizedCheckPrivilegesResponse = { + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + test(`does not add audit events when fully authorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; + + // Disable to test method + // eslint-disable-next-line dot-notation + await securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['y'])], + ]), + auditOptions: { + objects: auditObjects, + }, + }); + + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + + test(`does not add audit events when partially authorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; + + await expect( + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['x', 'y', 'z'])], + ['b', new Set(['x', 'y'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { + objects: auditObjects, + }, + }) + ).rejects.toThrowError('Unable to bulk_create b,c'); + + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + + test(`does not add audit events when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(unauthorizedCheckPrivilegesResponse); + + const auditObjects = [ + { type: 'a', id: '1' }, + { type: 'b', id: '2' }, + { type: 'c', id: '3' }, + ]; + + await expect( + // Disable to test method + // eslint-disable-next-line dot-notation + securityExtension['authorize']({ + actions, + types, + spaces, + enforceMap: new Map([ + ['a', new Set(['y', 'z'])], + ['b', new Set(['x', 'z'])], + ['c', new Set(['x', 'y'])], + ]), + auditOptions: { + objects: auditObjects, + }, + }) + ).rejects.toThrowError('Unable to bulk_create a,b,c'); + + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + }); +}); + +describe('#redactNamespaces', () => { + test(`filters namespaces that the user doesn't have access to`, () => { + const { securityExtension } = setup(); + + const typeMap = new Map().set('so-type', { + // redact is only concerned with 'login' attribute, not specific action + ['login:']: { authorizedSpaces: ['authorized-space'] }, + }); + + const so = { + id: 'some-id', + type: 'so-type', + namespaces: ['authorized-space', 'unauthorized-space'], + attributes: { + test: 'attr', + }, + score: 1, + references: [], + }; + + const result = securityExtension.redactNamespaces({ typeMap, savedObject: so }); + expect(result).toEqual(expect.objectContaining({ namespaces: ['authorized-space', '?'] })); + }); + + test(`does not redact on isGloballyAuthorized`, () => { + const { securityExtension } = setup(); + + const typeMap = new Map().set('so-type', { + // redact is only concerned with 'login' attribute, not specific action + ['login:']: { isGloballyAuthorized: true }, + }); + + const so = { + id: 'some-id', + type: 'so-type', + namespaces: ['space-a', 'space-b', 'space-c'], + attributes: { + test: 'attr', + }, + score: 1, + references: [], + }; + + const result = securityExtension.redactNamespaces({ typeMap, savedObject: so }); + expect(result).toEqual( + expect.objectContaining({ namespaces: ['space-a', 'space-b', 'space-c'] }) + ); + }); +}); + +describe('#create', () => { + const namespace = 'x'; + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + describe(`#authorizeCreate`, () => { + const actionString = 'create'; + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + await expect( + securityExtension.authorizeCreate({ + namespace: '', + object: obj1, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeCreate({ namespace, object: obj1 }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls internal authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + await securityExtension.authorizeCreate({ + namespace, + object: obj1, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + const expectedActions = new Set([SecurityAction.CREATE]); + const expectedSpaces = new Set([namespace, ...obj1.initialNamespaces!]); + const expectedTypes = new Set([obj1.type]); + const expectedEnforceMap = new Map>(); + expectedEnforceMap.set(obj1.type, new Set([namespace, ...obj1.initialNamespaces!])); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + options: { allowGlobalResource: true }, + auditOptions: { + objects: [obj1], + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: true }, + }); + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj1.type}/${actionString}`, 'login:'], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.CREATE, + typesAndSpaces: expectedEnforceMap, + typeMap: new Map().set(obj1.type, { + create: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + auditOptions: { objects: [obj1] }, + }); + }); + + test(`returns result when successful`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + const result = await securityExtension.authorizeCreate({ + namespace, + object: obj1, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: new Map().set(obj1.type, { + create: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + await securityExtension.authorizeCreate({ + namespace, + object: obj1, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'unknown', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `User is creating ${obj1.type} [id=${obj1.id}]`, + }); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeCreate({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to create ${obj1.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeCreate({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to create ${obj1.type}`); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to create a', + }, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'failure', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `Failed attempt to create ${obj1.type} [id=${obj1.id}]`, + }); + }); + }); + + describe(`#authorizeBulkCreate`, () => { + const actionString = 'bulk_create'; + const objects = [obj1, obj2, obj3, obj4]; + + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set(objects.map((obj) => obj.type)); + + const expectedActions = new Set([SecurityAction.BULK_CREATE]); + const expectedSpaces = new Set([ + namespace, + ...obj1.initialNamespaces!, + ...obj3.existingNamespaces!, + ...obj4.initialNamespaces!, + ...obj4.existingNamespaces!, + ]); + + const expectedEnforceMap = new Map([ + [obj1.type, new Set([namespace, ...obj1.initialNamespaces!])], + [obj2.type, new Set([namespace])], + [obj3.type, new Set([namespace])], + [obj4.type, new Set([namespace, ...obj4.initialNamespaces!])], + ]); + + const expectedTypeMap = new Map() + .set('a', { + bulk_create: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_create: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('c', { + bulk_create: { authorizedSpaces: ['x', 'bar'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('d', { + bulk_create: { authorizedSpaces: ['x', 'y', 'z'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `objects` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + const emptyObjects: AuthorizeCreateObject[] = []; + + await expect( + securityExtension.authorizeBulkCreate({ + namespace, + objects: emptyObjects, + }) + ).rejects.toThrowError('No objects specified for bulk_create authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkCreate({ + namespace: '', + objects: [obj1, obj2], + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeBulkCreate({ namespace, objects: [obj1] }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls internal authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeBulkCreate({ + namespace, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + options: { allowGlobalResource: true }, + auditOptions: { + objects, + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: true }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${obj1.type}/${actionString}`, + `mock-saved_object:${obj2.type}/${actionString}`, + `mock-saved_object:${obj3.type}/${actionString}`, + `mock-saved_object:${obj4.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.BULK_CREATE, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { objects }, + }); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkCreate({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_create', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_create', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkCreate({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map() + .set(obj1.type, { + bulk_create: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj2.type, { + bulk_create: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj3.type, { + bulk_create: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj4.type, { + bulk_create: { authorizedSpaces: ['x', 'y'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeBulkCreate({ + namespace, + objects, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'unknown', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User is creating ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkCreate({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_create ${obj2.type},${obj3.type},${obj4.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/create', authorized: false }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkCreate({ + namespace, + objects, + }) + ).rejects.toThrow( + `Unable to bulk_create ${obj1.type},${obj2.type},${obj3.type},${obj4.type}` + ); + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: `Unable to bulk_create ${obj1.type},${obj2.type},${obj3.type},${obj4.type}`, + }, + event: { + action: AuditAction.CREATE, + category: ['database'], + outcome: 'failure', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `Failed attempt to create ${obj.type} [id=${obj.id}]`, + }); + } + }); + }); +}); + +describe('update', () => { + const namespace = 'x'; + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + describe(`#authorizeUpdate`, () => { + const actionString = 'update'; + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + await expect( + securityExtension.authorizeUpdate({ + namespace: '', + object: obj2, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeUpdate({ namespace, object: obj1 }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls internal authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + await securityExtension.authorizeUpdate({ + namespace, + object: obj1, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + const expectedActions = new Set([SecurityAction.UPDATE]); + const expectedSpaces = new Set([namespace, obj1.objectNamespace!]); + const expectedTypes = new Set([obj1.type]); + const expectedEnforceMap = new Map>(); + expectedEnforceMap.set(obj1.type, new Set([namespace, obj1.objectNamespace!])); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + objects: [obj1], + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj1.type}/${actionString}`, 'login:'], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.UPDATE, + typesAndSpaces: expectedEnforceMap, + typeMap: new Map().set(obj1.type, { + update: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + auditOptions: { objects: [obj1] }, + }); + }); + + test(`returns result when successful`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + const result = await securityExtension.authorizeUpdate({ + namespace, + object: obj1, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: new Map().set(obj1.type, { + update: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + await securityExtension.authorizeUpdate({ + namespace, + object: obj1, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `User is updating ${obj1.type} [id=${obj1.id}]`, + }); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeUpdate({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to update ${obj1.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeUpdate({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to update ${obj1.type}`); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to update a', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `Failed attempt to update ${obj1.type} [id=${obj1.id}]`, + }); + }); + }); + + describe(`#authorizeBulkUpdate`, () => { + const actionString = 'bulk_update'; + const objects = [obj1, obj2, obj3, obj4]; + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_update', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:d/bulk_update', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_update', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set(objects.map((obj) => obj.type)); + + const expectedActions = new Set([SecurityAction.BULK_UPDATE]); + const expectedSpaces = new Set([ + namespace, + obj1.objectNamespace!, + ...obj3.existingNamespaces!, + obj4.objectNamespace!, + ...obj4.existingNamespaces!, + ]); + + const expectedEnforceMap = new Map([ + [obj1.type, new Set([namespace, obj1.objectNamespace!])], + [obj2.type, new Set([namespace])], + [obj3.type, new Set([namespace])], + [obj4.type, new Set([namespace, obj4.objectNamespace!])], + ]); + + const expectedTypeMap = new Map() + .set('a', { + bulk_update: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_update: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('c', { + bulk_update: { authorizedSpaces: ['x', 'bar'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('d', { + bulk_update: { authorizedSpaces: ['x', 'y', 'z'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `objects` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + const emptyObjects: AuthorizeUpdateObject[] = []; + + await expect( + securityExtension.authorizeBulkUpdate({ + namespace, + objects: emptyObjects, + }) + ).rejects.toThrowError('No objects specified for bulk_update authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkUpdate({ + namespace: '', + objects: [obj1, obj2], + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeBulkUpdate({ namespace, objects: [obj1] }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeBulkUpdate({ + namespace, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + objects, + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${obj1.type}/${actionString}`, + `mock-saved_object:${obj2.type}/${actionString}`, + `mock-saved_object:${obj3.type}/${actionString}`, + `mock-saved_object:${obj4.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.BULK_UPDATE, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { objects }, + }); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkUpdate({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_update', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_update', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_update', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_update', authorized: false }, + { resource: 'y', privilege: 'mock-saved_object:d/bulk_update', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_update', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkUpdate({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map() + .set(obj1.type, { + bulk_update: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj2.type, { + bulk_update: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj3.type, { + bulk_update: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj4.type, { + bulk_update: { authorizedSpaces: ['x', 'y'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeBulkUpdate({ + namespace, + objects, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User is updating ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkUpdate({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_update ${obj2.type},${obj3.type},${obj4.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeBulkUpdate({ + namespace, + objects, + }) + ).rejects.toThrow( + `Unable to bulk_update ${obj1.type},${obj2.type},${obj3.type},${obj4.type}` + ); + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: `Unable to bulk_update ${obj1.type},${obj2.type},${obj3.type},${obj4.type}`, + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `Failed attempt to update ${obj.type} [id=${obj.id}]`, + }); + } + }); + }); +}); + +describe('delete', () => { + const namespace = 'x'; + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + describe(`#authorizeDelete`, () => { + const actionString = 'delete'; + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + await expect( + securityExtension.authorizeDelete({ + namespace: '', + object: obj1, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeDelete({ namespace, object: obj1 }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls internal authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj3.type, actionString, true); + + await securityExtension.authorizeDelete({ + namespace, + object: obj3, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + const expectedActions = new Set([SecurityAction.DELETE]); + const expectedSpaces = new Set([namespace]); + const expectedTypes = new Set([obj3.type]); + const expectedEnforceMap = new Map>(); + expectedEnforceMap.set(obj3.type, new Set([namespace])); // obj3.existingNamespaces should NOT be included + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + objects: [{ ...obj3, existingNamespaces: [] }], + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj3.type}/${actionString}`, 'login:'], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.DELETE, + typesAndSpaces: expectedEnforceMap, + typeMap: new Map().set(obj3.type, { + delete: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + auditOptions: { + objects: [{ ...obj3, existingNamespaces: [] }], + }, + }); + }); + + test(`returns result when successful`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + const result = await securityExtension.authorizeDelete({ + namespace, + object: obj1, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: new Map().set(obj1.type, { + delete: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + await securityExtension.authorizeDelete({ + namespace, + object: obj1, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.DELETE, + category: ['database'], + outcome: 'unknown', + type: ['deletion'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `User is deleting ${obj1.type} [id=${obj1.id}]`, + }); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeDelete({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to delete ${obj1.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeDelete({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to delete ${obj1.type}`); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to delete a', + }, + event: { + action: AuditAction.DELETE, + category: ['database'], + outcome: 'failure', + type: ['deletion'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `Failed attempt to delete ${obj1.type} [id=${obj1.id}]`, + }); + }); + }); + + describe(`#authorizeBulkDelete`, () => { + const actionString = 'bulk_delete'; + const objects = [obj1, obj2, obj3, obj4]; + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_delete', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_delete', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_delete', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_delete', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_delete', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_delete', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set(objects.map((obj) => obj.type)); + + const expectedActions = new Set([SecurityAction.BULK_DELETE]); + const expectedSpaces = new Set([ + namespace, + ...obj3.existingNamespaces!, + ...obj4.existingNamespaces!, + ]); + + const expectedEnforceMap = new Map([ + [obj1.type, new Set([namespace])], + [obj2.type, new Set([namespace])], + [obj3.type, new Set([namespace])], + [obj4.type, new Set([namespace])], + ]); + + const expectedTypeMap = new Map() + .set('a', { + bulk_delete: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_delete: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('c', { + bulk_delete: { authorizedSpaces: ['x', 'bar'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('d', { + bulk_delete: { authorizedSpaces: ['x', 'z'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `objects` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + const emptyObjects: AuthorizeObjectWithExistingSpaces[] = []; + + await expect( + securityExtension.authorizeBulkDelete({ + namespace, + objects: emptyObjects, + }) + ).rejects.toThrowError('No objects specified for bulk_delete authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkDelete({ + namespace: '', + objects, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeBulkDelete({ namespace, objects }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeBulkDelete({ + namespace, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + objects, + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${obj1.type}/${actionString}`, + `mock-saved_object:${obj2.type}/${actionString}`, + `mock-saved_object:${obj3.type}/${actionString}`, + `mock-saved_object:${obj4.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.BULK_DELETE, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { objects }, + }); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkDelete({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_delete', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_delete', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_delete', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_delete', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_delete', authorized: false }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_delete', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkDelete({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map() + .set(obj1.type, { + bulk_delete: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj2.type, { + bulk_delete: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj3.type, { + bulk_delete: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj4.type, { + bulk_delete: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeBulkDelete({ + namespace, + objects, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.DELETE, + category: ['database'], + outcome: 'unknown', + type: ['deletion'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User is deleting ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_delete', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkDelete({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_delete ${obj2.type},${obj3.type},${obj4.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'bulk_delete', false); + + await expect( + securityExtension.authorizeBulkDelete({ + namespace, + objects, + }) + ).rejects.toThrow( + `Unable to bulk_delete ${obj1.type},${obj2.type},${obj3.type},${obj4.type}` + ); + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: `Unable to bulk_delete ${obj1.type},${obj2.type},${obj3.type},${obj4.type}`, + }, + event: { + action: AuditAction.DELETE, + category: ['database'], + outcome: 'failure', + type: ['deletion'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `Failed attempt to delete ${obj.type} [id=${obj.id}]`, + }); + } + }); + }); +}); + +describe('get', () => { + const namespace = 'x'; + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + describe(`#authorizeGet`, () => { + const actionString = 'get'; + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + await expect( + securityExtension.authorizeGet({ + namespace: '', + object: obj1, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeGet({ namespace, object: obj1 }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls internal authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj3.type, actionString, true); + + await securityExtension.authorizeGet({ + namespace, + object: obj3, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + const expectedActions = new Set([SecurityAction.GET]); + const expectedSpaces = new Set([namespace, ...obj3.existingNamespaces]); + const expectedTypes = new Set([obj3.type]); + const expectedEnforceMap = new Map>(); + expectedEnforceMap.set(obj3.type, new Set([namespace])); // obj3.existingNamespaces should NOT be included + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + bypass: 'never', + objects: [obj3], + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj3.type}/${actionString}`, 'login:'], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.GET, + typesAndSpaces: expectedEnforceMap, + typeMap: new Map().set(obj3.type, { + get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + auditOptions: { + bypass: 'never', + objects: [obj3], + }, + }); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/get', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + // setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + const result = await securityExtension.authorizeGet({ + namespace, + object: obj1, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map().set(obj1.type, { + get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + const result = await securityExtension.authorizeGet({ + namespace, + object: obj1, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: new Map().set(obj1.type, { + get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + await securityExtension.authorizeGet({ + namespace, + object: obj1, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.GET, + category: ['database'], + outcome: 'unknown', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `User is accessing ${obj1.type} [id=${obj1.id}]`, + }); + }); + + test(`does not add an audit event when successful if object is not found`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, true); + + await securityExtension.authorizeGet({ + namespace, + object: obj1, + objectNotFound: true, + }); + + expect(auditHelperSpy).not.toHaveBeenCalled(); + expect(addAuditEventSpy).not.toHaveBeenCalled(); + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeGet({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to get ${obj1.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeGet({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to get ${obj1.type}`); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to get a', + }, + event: { + action: AuditAction.GET, + category: ['database'], + outcome: 'failure', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `Failed attempt to access ${obj1.type} [id=${obj1.id}]`, + }); + }); + + test(`adds an audit event when unauthorized even if object is not found`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, actionString, false); + + await expect( + securityExtension.authorizeGet({ + namespace, + object: obj1, + objectNotFound: true, + }) + ).rejects.toThrow(`Unable to get ${obj1.type}`); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: 'Unable to get a', + }, + event: { + action: AuditAction.GET, + category: ['database'], + outcome: 'failure', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `Failed attempt to access ${obj1.type} [id=${obj1.id}]`, + }); + }); + }); + + describe(`#authorizeBulkGet`, () => { + const actionString = 'bulk_get'; + + const objA = { + ...obj1, + objectNamespaces: ['y', namespace], // include multiple spaces + }; + const objB = { ...obj2, objectNamespaces: ['z'], existingNamespaces: ['y'] }; // use a different namespace than the options namespace; + + const objects = [objA, objB]; + + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_get', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_get', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:b/bulk_get', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set(objects.map((obj) => obj.type)); + const expectedActions = new Set([SecurityAction.BULK_GET]); + const expectedSpaces = new Set([namespace, ...objA.objectNamespaces, ...objB.objectNamespaces]); + + const expectedEnforceMap = new Map([ + [obj1.type, new Set([namespace, ...objA.objectNamespaces])], + [obj2.type, new Set([namespace, ...objB.objectNamespaces])], + ]); + + const expectedTypeMap = new Map() + .set('a', { + bulk_get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_get: { + authorizedSpaces: ['x', 'y', 'z'], + }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `objects` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + const emptyObjects: AuthorizeBulkGetObject[] = []; + + await expect( + securityExtension.authorizeBulkGet({ + namespace, + objects: emptyObjects, + }) + ).rejects.toThrowError('No objects specified for bulk_get authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkGet({ + namespace: '', + objects, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect(securityExtension.authorizeBulkGet({ namespace, objects })).rejects.toThrowError( + 'Oh no!' + ); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeBulkGet({ + namespace, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { bypass: 'on_success', objects, useSuccessOutcome: true }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${objA.type}/${actionString}`, + `mock-saved_object:${objB.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.BULK_GET, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { bypass: 'on_success', objects, useSuccessOutcome: true }, + }); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkGet({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_get', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_get', authorized: false }, + { resource: 'z', privilege: 'mock-saved_object:b/bulk_get', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeBulkGet({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map() + .set(objA.type, { + bulk_get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(objB.type, { + bulk_get: { authorizedSpaces: ['x', 'z'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeBulkGet({ + namespace, + objects, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.GET, + category: ['database'], + outcome: 'success', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User has accessed ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`does not add an audit event for objects with an error when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeBulkGet({ + namespace, + objects: [objA, { ...objB, error: true }], + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.GET, + category: ['database'], + outcome: 'success', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: objA.type, id: objA.id }, + }, + message: `User has accessed ${objA.type} [id=${objA.id}]`, + }); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_get', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:b/bulk_get', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:b/bulk_get', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeBulkGet({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_get ${objB.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'bulk_delete', false); + + await expect( + securityExtension.authorizeBulkGet({ + namespace, + objects: [objA, { ...objB, error: true }], // setting error here to test the case that even err'd objects get an audit on failure + }) + ).rejects.toThrow(`Unable to bulk_get ${obj1.type},${obj2.type}`); + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: `Unable to bulk_get ${objA.type},${objB.type}`, + }, + event: { + action: AuditAction.GET, + category: ['database'], + outcome: 'failure', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `Failed attempt to access ${obj.type} [id=${obj.id}]`, + }); + } + }); + }); +}); + +describe(`#authorizeCheckConflicts`, () => { + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + const namespace = 'x'; + const actionString = 'bulk_create'; + const objects = [obj1, obj2, obj3, obj4]; + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set(objects.map((obj) => obj.type)); + + const expectedActions = new Set([SecurityAction.CHECK_CONFLICTS]); + const expectedSpaces = new Set([namespace]); + + const expectedEnforceMap = new Map([ + [obj1.type, new Set([namespace])], + [obj2.type, new Set([namespace])], + [obj3.type, new Set([namespace])], + [obj4.type, new Set([namespace])], + ]); + + const expectedTypeMap = new Map() + .set('a', { + bulk_create: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_create: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('c', { + bulk_create: { authorizedSpaces: ['x', 'bar'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('d', { + bulk_create: { authorizedSpaces: ['x', 'z'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `objects` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + const emptyObjects: AuthorizeObjectWithExistingSpaces[] = []; + + await expect( + securityExtension.authorizeCheckConflicts({ + namespace, + objects: emptyObjects, + }) + ).rejects.toThrowError('No objects specified for bulk_create authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeCheckConflicts({ + namespace: '', + objects, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeCheckConflicts({ namespace, objects }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeCheckConflicts({ + namespace, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { bypass: 'always' }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${obj1.type}/${actionString}`, + `mock-saved_object:${obj2.type}/${actionString}`, + `mock-saved_object:${obj3.type}/${actionString}`, + `mock-saved_object:${obj4.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.CHECK_CONFLICTS, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { bypass: 'always' }, + }); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeCheckConflicts({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:b/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:c/bulk_create', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:d/bulk_create', authorized: true }, + { resource: 'bar', privilege: 'mock-saved_object:c/bulk_create', authorized: false }, + { resource: 'z', privilege: 'mock-saved_object:d/bulk_create', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeCheckConflicts({ + namespace, + objects, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map() + .set(obj1.type, { + bulk_create: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj2.type, { + bulk_create: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj3.type, { + bulk_create: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(obj4.type, { + bulk_create: { authorizedSpaces: ['x'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`does not add any audit events when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeCheckConflicts({ + namespace, + objects, + }); + + expect(auditHelperSpy).not.toHaveBeenCalled(); + expect(addAuditEventSpy).not.toHaveBeenCalled(); + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_create', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeCheckConflicts({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_create ${obj2.type},${obj3.type},${obj4.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`does not add any audit events when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'bulk_create', false); + + await expect( + securityExtension.authorizeCheckConflicts({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_create ${obj1.type},${obj2.type},${obj3.type},${obj4.type}`); + expect(auditHelperSpy).not.toHaveBeenCalled(); + expect(addAuditEventSpy).not.toHaveBeenCalled(); + expect(auditLogger.log).not.toHaveBeenCalled(); + }); +}); + +describe(`#authorizeRemoveReferences`, () => { + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + const namespace = 'x'; + const actionString = 'delete'; + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/delete', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set([obj1.type]); + const expectedSpaces = new Set([namespace]); + const expectedEnforceMap = new Map([[obj1.type, new Set([namespace])]]); + + const expectedTypeMap = new Map().set('a', { + delete: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeRemoveReferences({ + namespace: '', + object: obj1, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeRemoveReferences({ namespace, object: obj1 }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeRemoveReferences({ + namespace, + object: obj1, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: new Set([SecurityAction.REMOVE_REFERENCES]), + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + objects: [obj1], + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj1.type}/${actionString}`, 'login:'], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.REMOVE_REFERENCES, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { objects: [obj1] }, + }); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeRemoveReferences({ + namespace, + object: obj1, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/delete', authorized: true }, + { privilege: 'login:', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeRemoveReferences({ + namespace, + object: obj1, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map().set(obj1.type, { + delete: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeRemoveReferences({ + namespace, + object: obj1, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.REMOVE_REFERENCES, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `User is removing references to ${obj1.type} [id=${obj1.id}]`, + }); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'delete', false); + + await expect( + securityExtension.authorizeRemoveReferences({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to delete ${obj1.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'delete', false); + + await expect( + securityExtension.authorizeRemoveReferences({ + namespace, + object: obj1, + }) + ).rejects.toThrow(`Unable to delete ${obj1.type}`); + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { code: 'Error', message: `Unable to delete ${obj1.type}` }, + event: { + action: AuditAction.REMOVE_REFERENCES, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj1.type, id: obj1.id }, + }, + message: `Failed attempt to remove references to ${obj1.type} [id=${obj1.id}]`, + }); + }); +}); + +describe(`#authorizeOpenPointInTime`, () => { + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + const namespace = 'x'; + const actionString = 'open_point_in_time'; + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/open_point_in_time', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set([obj1.type]); + const expectedSpaces = new Set([namespace]); + const expectedTypeMap = new Map().set('a', { + open_point_in_time: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `namespaces` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeOpenPointInTime({ + namespaces: new Set(), + types: expectedTypes, + }) + ).rejects.toThrowError('No spaces specified for authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `types` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: new Set(), + }) + ).rejects.toThrowError('No types specified for authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: expectedTypes, + }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and no enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: new Set([SecurityAction.OPEN_POINT_IN_TIME]), + types: expectedTypes, + spaces: expectedSpaces, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj1.type}/${actionString}`, 'login:'], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/open_point_in_time', authorized: true }, + { privilege: 'login:', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map().set(obj1.type, { + open_point_in_time: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test(`adds audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + + expect(auditHelperSpy).not.toHaveBeenCalled(); // The helper is not called, as open PIT calls the addAudit method directly + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.OPEN_POINT_IN_TIME, + category: ['database'], + outcome: 'unknown', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: `User is opening point-in-time saved objects`, + }); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'delete', false); + + await expect( + securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: expectedTypes, + }) + ).rejects.toThrow(`unauthorized`); + }); + + test(`adds audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'open_point_in_time', false); + + await expect( + securityExtension.authorizeOpenPointInTime({ + namespaces: expectedSpaces, + types: expectedTypes, + }) + ).rejects.toThrow(`unauthorized`); + expect(auditHelperSpy).not.toHaveBeenCalled(); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { code: 'Error', message: `User is unauthorized for any requested types/spaces` }, + event: { + action: AuditAction.OPEN_POINT_IN_TIME, + category: ['database'], + outcome: 'failure', + type: ['creation'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: `Failed attempt to open point-in-time saved objects`, + }); + }); +}); + +describe(`#auditClosePointInTime`, () => { + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + test(`adds audit event`, async () => { + const { securityExtension, auditLogger } = setup(); + securityExtension.auditClosePointInTime(); + + expect(auditHelperSpy).not.toHaveBeenCalled(); // The helper is not called, as close PIT calls the addAudit method directly + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.CLOSE_POINT_IN_TIME, + category: ['database'], + outcome: 'unknown', + type: ['deletion'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: `User is closing point-in-time saved objects`, + }); + }); +}); + +describe('#authorizeAndRedactMultiNamespaceReferences', () => { + const namespace = 'x'; + + const refObj1 = { + id: 'id-1', + inboundReferences: [], + originId: undefined, + spaces: ['default', 'space-1'], + spacesWithMatchingAliases: ['space-2', 'space-3', 'space-4'], + spacesWithMatchingOrigins: undefined, + type: 'a', + }; + const refObj2 = { + id: 'id-2', + inboundReferences: [], + originId: undefined, + spaces: ['default', 'space-2'], + spacesWithMatchingAliases: undefined, + spacesWithMatchingOrigins: ['space-1', 'space-3'], + type: 'b', + }; + const refObj3 = { + id: 'id-3', + inboundReferences: [{ id: 'id-1', name: 'ref-name', type: 'a' }], + originId: undefined, + spaces: ['default', 'space-1', 'space-4'], + spacesWithMatchingAliases: undefined, + spacesWithMatchingOrigins: undefined, + type: 'c', + }; + const objects = [refObj1, refObj2, refObj3]; + + const expectedTypes = new Set(objects.map((obj) => obj.type)); + const expectedSpaces = new Set([ + namespace, + ...refObj1.spaces, + ...refObj1.spacesWithMatchingAliases, + ...refObj2.spaces, + ...refObj2.spacesWithMatchingOrigins, + ...refObj3.spaces, + ]); + + const expectedEnforceMap = new Map([ + [refObj1.type, new Set([namespace])], + [refObj2.type, new Set([namespace])], + [refObj3.type, new Set([namespace])], + ]); + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + // NOTE: This comment should inform our test cases... + // Now, filter/redact the results. Most SOR functions just redact the `namespaces` field from each returned object. However, this function + // will actually filter the returned object graph itself. + // This is done in two steps: (1) objects which the user can't access *in this space* are filtered from the graph, and the + // graph is rearranged to avoid leaking information. (2) any spaces that the user can't access are redacted from each individual object. + // After we finish filtering, we can write audit events for each object that is going to be returned to the user. + + describe('purpose `collectMultiNamespaceReferences`', () => { + const actionString = 'bulk_get'; + + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { privilege: 'login:', authorized: true }, + { + resource: 'x', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'space-1', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'space-2', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'space-3', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'x', + privilege: 'mock-saved_object:c/bulk_get', + authorized: true, + }, + { + resource: 'space-1', + privilege: 'mock-saved_object:c/bulk_get', + authorized: true, + }, + { + resource: 'space-4', + privilege: 'mock-saved_object:c/bulk_get', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse; + + const partiallyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + // { privilege: 'login:', authorized: true }, + { + resource: 'x', + privilege: 'login:', + authorized: true, + }, + { + resource: 'space-1', + privilege: 'login:', + authorized: true, + }, + { + resource: 'space-2', + privilege: 'login:', + authorized: true, + }, + { + resource: 'x', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'space-1', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'space-2', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'x', + privilege: 'mock-saved_object:c/bulk_get', + authorized: true, + }, + { + resource: 'space-1', + privilege: 'mock-saved_object:c/bulk_get', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse; + + const redactedObjects = [ + { + ...refObj1, + spaces: ['space-1', '?'], + spacesWithMatchingAliases: ['space-2', '?', '?'], + }, + { ...refObj2, spaces: ['space-2', '?'], spacesWithMatchingOrigins: ['space-1', '?'] }, + { ...refObj3, spaces: ['space-1', '?', '?'] }, + ]; + const expectedActions = new Set([SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES]); + + const fullyAuthorizedTypeMap = new Map() + .set('a', { + bulk_get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_get: { authorizedSpaces: ['x', 'space-1', 'space-2', 'space-3'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('c', { + bulk_get: { authorizedSpaces: ['x', 'space-1', 'space-4'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + const partiallyAuthorizedTypeMap = new Map() + .set('a', { + bulk_get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { authorizedSpaces: ['x', 'space-1', 'space-2'] }, + }) + .set('b', { + bulk_get: { authorizedSpaces: ['x', 'space-1', 'space-2'] }, + ['login:']: { authorizedSpaces: ['x', 'space-1', 'space-2'] }, + }) + .set('c', { + bulk_get: { authorizedSpaces: ['x', 'space-1'] }, + ['login:']: { authorizedSpaces: ['x', 'space-1', 'space-2'] }, + }); + + test('returns empty array when no objects are provided`', async () => { + const { securityExtension } = setup(); + const emptyObjects: SavedObjectReferenceWithContext[] = []; + + const result = await securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects: emptyObjects, + }); + expect(result).toEqual(emptyObjects); + }); + + test('throws an error when `namespace` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + await expect( + securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace: '', + objects: [refObj1, refObj2], + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + bypass: 'on_success', + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${refObj1.type}/${actionString}`, + `mock-saved_object:${refObj2.type}/${actionString}`, + `mock-saved_object:${refObj3.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(4); + // Called once with complete enforce map (bypasses audit on success) + expect(enforceAuthorizationSpy).toHaveBeenNthCalledWith(1, { + action: SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES, + typesAndSpaces: expectedEnforceMap, + typeMap: partiallyAuthorizedTypeMap, + auditOptions: { bypass: 'on_success' }, + }); + // Called once per object afterward + let i = 2; + for (const obj of objects) { + expect(enforceAuthorizationSpy).toHaveBeenNthCalledWith(i++, { + action: SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES, + typesAndSpaces: new Map([[obj.type, new Set([namespace])]]), + typeMap: partiallyAuthorizedTypeMap, + auditOptions: { bypass: 'always' }, + }); + } + }); + + test(`returns unredacted result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + const result = await securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + }); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(4); + expect(enforceAuthorizationSpy).toHaveBeenNthCalledWith(1, { + action: SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES, + typesAndSpaces: expectedEnforceMap, + typeMap: fullyAuthorizedTypeMap, + auditOptions: { bypass: 'on_success' }, + }); + let i = 2; + for (const obj of objects) { + expect(enforceAuthorizationSpy).toHaveBeenNthCalledWith(i++, { + action: SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES, + typesAndSpaces: new Map([[obj.type, new Set([namespace])]]), + typeMap: fullyAuthorizedTypeMap, + auditOptions: { bypass: 'always' }, + }); + } + expect(result).toEqual(objects); + }); + + test(`returns redacted result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + const result = await securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + }); + expect(redactNamespacesSpy).toHaveBeenCalledTimes(5); // spaces x3, spaces of aliases x1, spaces of origins x1 + expect(result).toEqual(redactedObjects); + }); + + test(`adds an audit event per object when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + await securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + }); + + // the audit helper is not called during this action + // the addAuditEvent method is called directly + expect(auditHelperSpy).not.toHaveBeenCalled(); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + let i = 1; + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: undefined, + event: { + action: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, + category: ['database'], + outcome: 'success', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User has collected references and spaces of ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_get ${refObj2.type},${refObj3.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds a single audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [{ privilege: 'login:', authorized: true }], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_get ${refObj1.type},${refObj2.type},${refObj3.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: `Unable to bulk_get ${refObj1.type},${refObj2.type},${refObj3.type}`, + }, + event: { + action: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, + category: ['database'], + outcome: 'failure', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: `Failed attempt to collect references and spaces of saved objects`, + }); + }); + }); + + describe('purpose `updateObjectsSpaces`', () => { + const actionString = 'share_to_space'; + const purpose = 'updateObjectsSpaces'; + + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: true }, + { privilege: 'login:', authorized: true }, + { + resource: 'x', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'space-1', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'space-2', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'space-3', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'x', + privilege: 'mock-saved_object:c/share_to_space', + authorized: true, + }, + { + resource: 'space-1', + privilege: 'mock-saved_object:c/share_to_space', + authorized: true, + }, + { + resource: 'space-4', + privilege: 'mock-saved_object:c/share_to_space', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedActions = new Set([ + SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES_UPDATE_SPACES, + ]); + + const fullyAuthorizedTypeMap = new Map() + .set('a', { + share_to_space: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + share_to_space: { authorizedSpaces: ['x', 'space-1', 'space-2', 'space-3'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('c', { + share_to_space: { authorizedSpaces: ['x', 'space-1', 'space-4'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeAndRedactMultiNamespaceReferences({ + namespace, + objects, + options: { purpose }, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + bypass: 'on_success', + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${refObj1.type}/${actionString}`, + `mock-saved_object:${refObj2.type}/${actionString}`, + `mock-saved_object:${refObj3.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(4); + // Called once with complete enforce map (bypasses audit on success) + expect(enforceAuthorizationSpy).toHaveBeenNthCalledWith(1, { + action: SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES_UPDATE_SPACES, + typesAndSpaces: expectedEnforceMap, + typeMap: fullyAuthorizedTypeMap, + auditOptions: { bypass: 'on_success' }, + }); + // Called once per object afterward + let i = 2; + for (const obj of objects) { + expect(enforceAuthorizationSpy).toHaveBeenNthCalledWith(i++, { + action: SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES_UPDATE_SPACES, + typesAndSpaces: new Map([[obj.type, new Set([namespace])]]), + typeMap: fullyAuthorizedTypeMap, + auditOptions: { bypass: 'always' }, + }); + } + }); + }); +}); + +describe('#authorizeAndRedactInternalBulkResolve', () => { + const namespace = 'x'; + + const resolveObj1: SavedObjectsResolveResponse = { + outcome: 'exactMatch', + saved_object: { + attributes: {}, + id: '13', + namespaces: ['foo'], + references: [], + type: 'a', + }, + }; + const resolveObj2: SavedObjectsResolveResponse = { + outcome: 'exactMatch', + saved_object: { + attributes: {}, + id: '14', + namespaces: ['bar'], + references: [], + type: 'b', + }, + }; + + const objects = [resolveObj1, resolveObj2]; + + const expectedTypes = new Set(objects.map((obj) => obj.saved_object.type)); + const expectedSpaces = new Set(['foo', 'bar', namespace]); + + const expectedEnforceMap = new Map([ + [resolveObj1.saved_object.type, new Set([namespace])], + [resolveObj2.saved_object.type, new Set([namespace])], + ]); + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + const actionString = 'bulk_get'; + + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { privilege: 'login:', authorized: true }, + { + resource: 'x', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'bar', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse; + + const partiallyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { + resource: 'x', + privilege: 'login:', + authorized: true, + }, + { + resource: 'foo', + privilege: 'login:', + authorized: true, + }, + { + resource: 'x', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + { + resource: 'bar', + privilege: 'mock-saved_object:b/bulk_get', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse; + + const redactedObjects = [ + resolveObj1, + { ...resolveObj2, saved_object: { ...resolveObj2.saved_object, namespaces: ['?'] } }, + ]; + const expectedActions = new Set([SecurityAction.INTERNAL_BULK_RESOLVE]); + + const fullyAuthorizedTypeMap = new Map() + .set('a', { + bulk_get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_get: { authorizedSpaces: ['x', 'bar'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + const partiallyAuthorizedTypeMap = new Map() + .set('a', { + bulk_get: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { authorizedSpaces: ['x', 'foo'] }, + }) + .set('b', { + bulk_get: { authorizedSpaces: ['x', 'bar'] }, + ['login:']: { authorizedSpaces: ['x', 'foo'] }, + }); + + test('returns empty array when no objects are provided`', async () => { + const { securityExtension } = setup(); + const emptyObjects: Array | BulkResolveError> = []; + + const result = await securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects: emptyObjects, + }); + expect(result).toEqual(emptyObjects); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects, + }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { + useSuccessOutcome: true, + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${resolveObj1.saved_object.type}/${actionString}`, + `mock-saved_object:${resolveObj2.saved_object.type}/${actionString}`, + 'login:', + ], + expect.arrayContaining([...expectedSpaces]) + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.INTERNAL_BULK_RESOLVE, + typesAndSpaces: expectedEnforceMap, + typeMap: partiallyAuthorizedTypeMap, + auditOptions: { useSuccessOutcome: true }, + }); + }); + + test(`returns unredacted result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + const result = await securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects, + }); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.INTERNAL_BULK_RESOLVE, + typesAndSpaces: expectedEnforceMap, + typeMap: fullyAuthorizedTypeMap, + auditOptions: { useSuccessOutcome: true }, + }); + expect(result).toEqual(objects); + }); + + test(`returns redacted result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); // Return any well-formed response to avoid an unhandled error + + const result = await securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects, + }); + expect(redactNamespacesSpy).toHaveBeenCalledTimes(2); + expect(result).toEqual(redactedObjects); + }); + + test(`adds an audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(auditHelperSpy).toHaveBeenCalledWith({ + action: 'saved_object_resolve', + objects: undefined, + useSuccessOutcome: true, + }); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledWith({ + action: 'saved_object_resolve', + addToSpaces: undefined, + deleteFromSpaces: undefined, + error: undefined, + outcome: 'success', + }); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.RESOLVE, + category: ['database'], + outcome: 'success', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: `User has resolved saved objects`, + }); + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_get', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects, + }) + ).rejects.toThrow(`Unable to bulk_get ${resolveObj2.saved_object.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [{ privilege: 'login:', authorized: true }], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeAndRedactInternalBulkResolve({ + namespace, + objects, + }) + ).rejects.toThrow( + `Unable to bulk_get ${resolveObj1.saved_object.type},${resolveObj2.saved_object.type}` + ); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { + code: 'Error', + message: `Unable to bulk_get ${resolveObj1.saved_object.type},${resolveObj2.saved_object.type}`, + }, + event: { + action: AuditAction.RESOLVE, + category: ['database'], + outcome: 'failure', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: `Failed attempt to resolve saved objects`, + }); + }); +}); + +describe('#authorizeUpdateSpaces', () => { + const namespace = 'x'; + + const multiSpaceObj1 = { + type: 'a', + id: '1', + existingNamespaces: ['add_space_1', 'add_space_2'], + }; + const multiSpaceObj2 = { + type: 'b', + id: '2', + existingNamespaces: ['*'], + }; + const multiSpaceObj3 = { + type: 'a', + id: '3', + existingNamespaces: ['rem_space_2', 'add_space_2'], + }; + const multiSpaceObj4 = { + type: 'b', + id: '4', + existingNamespaces: ['foo', 'add_space_1'], + }; + + const objects = [multiSpaceObj1, multiSpaceObj2, multiSpaceObj3, multiSpaceObj4]; + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + const actionString = 'share_to_space'; + + const spacesToAdd = ['add_space_1', 'add_space_2']; + const spacesToRemove = ['rem_space_1', 'rem_space_2']; + + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: true }, + { privilege: 'login:', authorized: true }, + { + resource: 'x', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'add_space_1', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'add_space_2', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'rem_space_1', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'rem_space_2', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set(objects.map((obj) => obj.type)); + + const expectedActions = new Set([SecurityAction.UPDATE_OBJECTS_SPACES]); + const expectedSpaces = new Set([...spacesToAdd, ...spacesToRemove, namespace, 'foo']); + + const expectedEnforceMap = new Map([ + [multiSpaceObj1.type, new Set([...spacesToAdd, ...spacesToRemove, namespace])], + [multiSpaceObj2.type, new Set([...spacesToAdd, ...spacesToRemove, namespace])], + ]); + + const expectedTypeMap = new Map() + .set('a', { + share_to_space: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + share_to_space: { authorizedSpaces: ['x', ...spacesToAdd, ...spacesToRemove] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `objects` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + const emptyObjects: AuthorizeObjectWithExistingSpaces[] = []; + + await expect( + securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects: emptyObjects, + }) + ).rejects.toThrowError('No objects specified for share_to_space authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `namespace` is an empty string', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeUpdateSpaces({ + namespace: '', + spacesToAdd, + spacesToRemove, + objects, + }) + ).rejects.toThrowError('namespace cannot be an empty string'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeUpdateSpaces({ namespace, spacesToAdd, spacesToRemove, objects }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + options: { allowGlobalResource: true }, + auditOptions: { + objects, + addToSpaces: spacesToAdd, + deleteFromSpaces: spacesToRemove, + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: true }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${multiSpaceObj1.type}/${actionString}`, + `mock-saved_object:${multiSpaceObj2.type}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.UPDATE_OBJECTS_SPACES, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { objects, addToSpaces: spacesToAdd, deleteFromSpaces: spacesToRemove }, + }); + }); + + test(`calls authorize methods with '*' when spacesToAdd includes '*'`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: true }, + { privilege: 'mock-saved_object:b/share_to_space', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd: ['*'], + spacesToRemove, + objects, + }); + + const spaces = new Set(['*', ...spacesToRemove, 'x', 'add_space_1', 'add_space_2', 'foo']); + const enforceMap = new Map([ + [multiSpaceObj1.type, new Set(['*', ...spacesToRemove, namespace])], + [multiSpaceObj2.type, new Set(['*', ...spacesToRemove, namespace])], + ]); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces, + enforceMap, + options: { allowGlobalResource: true }, + auditOptions: { + objects, + addToSpaces: ['*'], + deleteFromSpaces: spacesToRemove, + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces, + types: expectedTypes, + options: { allowGlobalResource: true }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${multiSpaceObj1.type}/${actionString}`, + `mock-saved_object:${multiSpaceObj2.type}/${actionString}`, + 'login:', + ], + [...spaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.UPDATE_OBJECTS_SPACES, + typesAndSpaces: enforceMap, + typeMap: new Map() + .set('a', { + share_to_space: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + share_to_space: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + auditOptions: { objects, addToSpaces: ['*'], deleteFromSpaces: spacesToRemove }, + }); + }); + + test(`calls authorize methods with '*' when spacesToRemove includes '*'`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: true }, + { privilege: 'mock-saved_object:b/share_to_space', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove: ['*'], + objects, + }); + + const spaces = new Set([...spacesToAdd, '*', 'x', 'rem_space_2', 'foo']); + const enforceMap = new Map([ + [multiSpaceObj1.type, new Set([...spacesToAdd, '*', namespace])], + [multiSpaceObj2.type, new Set([...spacesToAdd, '*', namespace])], + ]); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces, + enforceMap, + options: { allowGlobalResource: true }, + auditOptions: { + objects, + addToSpaces: spacesToAdd, + deleteFromSpaces: ['*'], + }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces, + types: expectedTypes, + options: { allowGlobalResource: true }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${multiSpaceObj1.type}/${actionString}`, + `mock-saved_object:${multiSpaceObj2.type}/${actionString}`, + 'login:', + ], + [...spaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.UPDATE_OBJECTS_SPACES, + typesAndSpaces: enforceMap, + typeMap: new Map() + .set('a', { + share_to_space: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + share_to_space: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + auditOptions: { objects, addToSpaces: spacesToAdd, deleteFromSpaces: ['*'] }, + }); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: true }, + { privilege: 'login:', authorized: true }, + { + resource: 'x', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'foo', + privilege: 'mock-saved_object:b/share_to_space', + authorized: false, + }, + { + resource: 'add_space_1', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'add_space_2', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'rem_space_1', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + { + resource: 'rem_space_2', + privilege: 'mock-saved_object:b/share_to_space', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map() + .set(multiSpaceObj1.type, { + share_to_space: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set(multiSpaceObj2.type, { + share_to_space: { authorizedSpaces: ['x', ...spacesToAdd, ...spacesToRemove] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects, + }); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + let i = 1; + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: undefined, + event: { + action: AuditAction.UPDATE_OBJECTS_SPACES, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User is updating spaces of ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects, + }) + ).rejects.toThrow(`Unable to share_to_space ${multiSpaceObj2.type}`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: false }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeUpdateSpaces({ + namespace, + spacesToAdd, + spacesToRemove, + objects, + }) + ).rejects.toThrow(`Unable to share_to_space ${multiSpaceObj1.type},${multiSpaceObj2.type}`); + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + let i = 1; + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: { + code: 'Error', + message: `Unable to share_to_space ${multiSpaceObj1.type},${multiSpaceObj2.type}`, + }, + event: { + action: AuditAction.UPDATE_OBJECTS_SPACES, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: spacesToAdd, + delete_from_spaces: spacesToRemove, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `Failed attempt to update spaces of ${obj.type} [id=${obj.id}]`, + }); + } + }); +}); + +describe('find', () => { + const namespace = 'x'; + const actionString = 'find'; + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/find', authorized: true }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set([obj1.type]); + const expectedSpaces = new Set([namespace]); + const expectedTypeMap = new Map().set('a', { + find: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + describe(`#authorizeFind`, () => { + test('throws an error when `namespaces` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeFind({ + namespaces: new Set(), + types: expectedTypes, + }) + ).rejects.toThrowError('No spaces specified for authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when `types` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeFind({ + namespaces: expectedSpaces, + types: new Set(), + }) + ).rejects.toThrowError('No types specified for authorization'); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeFind({ namespaces: expectedSpaces, types: expectedTypes }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and no enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeFind({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: new Set([SecurityAction.FIND]), + types: expectedTypes, + spaces: expectedSpaces, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj1.type}/${actionString}`, 'login:'], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.authorizeFind({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + expect(result).toEqual({ + status: 'fully_authorized', + typeMap: expectedTypeMap, + }); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/find', authorized: true }, + { privilege: 'login:', authorized: false }, + ], + }, + } as CheckPrivilegesResponse); + + const result = await securityExtension.authorizeFind({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + expect(result).toEqual({ + status: 'partially_authorized', + typeMap: new Map().set(obj1.type, { + find: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test(`does not add audit event when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeFind({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + + expect(auditHelperSpy).not.toHaveBeenCalled(); // The helper is not called, as authorizeFind calls the addAudit method directly + expect(addAuditEventSpy).not.toHaveBeenCalled(); + expect(auditLogger.log).not.toHaveBeenCalled(); + }); + + test(`returns result when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'find', false); + + const result = await securityExtension.authorizeFind({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + expect(result).toEqual({ + status: 'unauthorized', + typeMap: new Map().set(obj1.type, { + 'login:': { isGloballyAuthorized: true, authorizedSpaces: [] }, + }), + }); + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test(`adds audit event when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'find', false); + + await securityExtension.authorizeFind({ + namespaces: expectedSpaces, + types: expectedTypes, + }); + expect(auditHelperSpy).not.toHaveBeenCalled(); + expect(addAuditEventSpy).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith({ + error: { code: 'Error', message: `User is unauthorized for any requested types/spaces` }, + event: { + action: AuditAction.FIND, + category: ['database'], + outcome: 'failure', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: undefined, + }, + message: `Failed attempt to access saved objects`, + }); + }); + }); + + describe(`#getFindRedactTypeMap`, () => { + const existingNamespaces = [namespace, 'y', 'z', 'foo']; + const objects = [ + { type: obj1.type, id: obj1.id, existingNamespaces: [namespace, 'y'] }, + { type: obj1.type, id: obj2.id, existingNamespaces: [namespace, 'z'] }, + { type: obj1.type, id: obj3.id, existingNamespaces: [namespace, 'foo'] }, + ]; + + const partiallyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'login:', authorized: true }, + { resource: 'x', privilege: 'mock-saved_object:a/find', authorized: true }, + { resource: 'y', privilege: 'mock-saved_object:a/find', authorized: false }, + { resource: 'z', privilege: 'mock-saved_object:a/find', authorized: true }, + { resource: 'foo', privilege: 'mock-saved_object:a/find', authorized: false }, + ], + }, + } as CheckPrivilegesResponse; + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects: [{ type: obj1.type, id: obj1.id, existingNamespaces }], + }) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and no enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects, + }); + + const updateExpectedSpaces = new Set(existingNamespaces); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: new Set([SecurityAction.FIND]), + types: expectedTypes, + spaces: updateExpectedSpaces, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: updateExpectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [`mock-saved_object:${obj1.type}/${actionString}`, 'login:'], + [...updateExpectedSpaces] + ); + + expect(enforceAuthorizationSpy).not.toHaveBeenCalled(); + }); + + test('returns undefined if there are no additional spaces', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects: [{ type: obj1.type, id: obj1.id, existingNamespaces: [namespace] }], + }); + expect(result).toBeUndefined(); + }); + + test(`returns result when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects, + }); + + expect(result).toEqual(expectedTypeMap); + }); + + test(`returns result when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(partiallyAuthorizedCheckPrivilegesResponse); + + const result = await securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects, + }); + + expect(result).toEqual( + new Map().set('a', { + find: { authorizedSpaces: ['x', 'z'] }, + 'login:': { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + ); + }); + + test(`returns result when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'find', false); + + const result = await securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects, + }); + + expect(result).toEqual( + new Map().set(obj1.type, { + 'login:': { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + ); + }); + + test(`adds an audit event per object when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'find', false); + + await securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects, + }); + + expect(auditHelperSpy).not.toHaveBeenCalled(); // The helper is not called, as getFindRedactTypeMap calls the addAudit method directly + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.FIND, + category: ['database'], + outcome: 'success', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User has accessed ${obj.type} [id=${obj.id}]`, + }); + } + }); + + test(`adds an audit event per object when authorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, obj1.type, 'find', true); + + await securityExtension.getFindRedactTypeMap({ + previouslyCheckedNamespaces: expectedSpaces, + objects, + }); + + expect(auditHelperSpy).not.toHaveBeenCalled(); // The helper is not called, as getFindRedactTypeMap calls the addAudit method directly + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length); + for (const obj of objects) { + expect(auditLogger.log).toHaveBeenCalledWith({ + error: undefined, + event: { + action: AuditAction.FIND, + category: ['database'], + outcome: 'success', + type: ['access'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: obj.type, id: obj.id }, + }, + message: `User has accessed ${obj.type} [id=${obj.id}]`, + }); + } + }); + }); +}); + +describe('#authorizeDisableLegacyUrlAliases', () => { + const legacyUrlAlias1: LegacyUrlAliasTarget = { + targetType: 'a', + targetSpace: 'x', + sourceId: 'id1', + }; + const legacyUrlAlias2: LegacyUrlAliasTarget = { + targetType: 'b', + targetSpace: 'y', + sourceId: 'id2', + }; + const legacyUrlAlias3: LegacyUrlAliasTarget = { + targetType: 'c', + targetSpace: 'z', + sourceId: 'id3', + }; + const legacyUrlAliases: LegacyUrlAliasTarget[] = [ + legacyUrlAlias1, + legacyUrlAlias2, + legacyUrlAlias3, + ]; + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + const actionString = 'bulk_update'; + + const fullyAuthorizedCheckPrivilegesResponse = { + hasAllRequested: true, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, + { privilege: 'login:', authorized: true }, + { + resource: 'y', + privilege: 'mock-saved_object:b/bulk_update', + authorized: true, + }, + { + resource: 'z', + privilege: 'mock-saved_object:c/bulk_update', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse; + + const expectedTypes = new Set(legacyUrlAliases.map((alias) => alias.targetType)); + const expectedActions = new Set([SecurityAction.BULK_UPDATE]); + const expectedSpaces = new Set(legacyUrlAliases.map((alias) => alias.targetSpace)); + + const expectedEnforceMap = new Map([ + [legacyUrlAlias1.targetType, new Set([legacyUrlAlias1.targetSpace])], + [legacyUrlAlias2.targetType, new Set([legacyUrlAlias2.targetSpace])], + [legacyUrlAlias3.targetType, new Set([legacyUrlAlias3.targetSpace])], + ]); + + const auditObjects = legacyUrlAliases.map((alias) => { + return { + type: 'legacy-url-alias', + id: `${alias.targetSpace}:${alias.targetType}:${alias.sourceId}`, + }; + }); + + const expectedTypeMap = new Map() + .set('a', { + bulk_update: { isGloballyAuthorized: true, authorizedSpaces: [] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('b', { + bulk_update: { authorizedSpaces: ['y'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }) + .set('c', { + bulk_update: { authorizedSpaces: ['z'] }, + ['login:']: { isGloballyAuthorized: true, authorizedSpaces: [] }, + }); + + test('throws an error when `aliases` is empty', async () => { + const { securityExtension, checkPrivileges } = setup(); + await expect(securityExtension.authorizeDisableLegacyUrlAliases([])).rejects.toThrowError( + 'No aliases specified for authorization' + ); + expect(checkPrivileges).not.toHaveBeenCalled(); + }); + + test('throws an error when checkAuthorization fails', async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockRejectedValue(new Error('Oh no!')); + + await expect( + securityExtension.authorizeDisableLegacyUrlAliases(legacyUrlAliases) + ).rejects.toThrowError('Oh no!'); + }); + + test(`calls authorize methods with expected actions, types, spaces, and enforce map`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeDisableLegacyUrlAliases(legacyUrlAliases); + + expect(authorizeSpy).toHaveBeenCalledTimes(1); + expect(authorizeSpy).toHaveBeenCalledWith({ + actions: expectedActions, + types: expectedTypes, + spaces: expectedSpaces, + enforceMap: expectedEnforceMap, + auditOptions: { objects: auditObjects }, + }); + + expect(checkAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(checkAuthorizationSpy).toHaveBeenCalledWith({ + actions: new Set([actionString]), + spaces: expectedSpaces, + types: expectedTypes, + options: { allowGlobalResource: false }, + }); + + expect(checkPrivileges).toHaveBeenCalledTimes(1); + expect(checkPrivileges).toHaveBeenCalledWith( + [ + `mock-saved_object:${legacyUrlAlias1.targetType}/${actionString}`, + `mock-saved_object:${legacyUrlAlias2.targetType}/${actionString}`, + `mock-saved_object:${legacyUrlAlias3.targetType}/${actionString}`, + 'login:', + ], + [...expectedSpaces] + ); + + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + expect(enforceAuthorizationSpy).toHaveBeenCalledWith({ + action: SecurityAction.BULK_UPDATE, + typesAndSpaces: expectedEnforceMap, + typeMap: expectedTypeMap, + auditOptions: { objects: auditObjects }, + }); + }); + + test(`returns when fully authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await expect( + securityExtension.authorizeDisableLegacyUrlAliases(legacyUrlAliases) + ).resolves.toBe(undefined); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`returns when partially authorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/bulk_update', authorized: true }, + { privilege: 'login:', authorized: false }, + { + resource: 'y', + privilege: 'mock-saved_object:b/bulk_update', + authorized: true, + }, + { + resource: 'z', + privilege: 'mock-saved_object:c/bulk_update', + authorized: true, + }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeDisableLegacyUrlAliases(legacyUrlAliases) + ).resolves.toBe(undefined); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when successful`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue(fullyAuthorizedCheckPrivilegesResponse); + + await securityExtension.authorizeDisableLegacyUrlAliases(legacyUrlAliases); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(legacyUrlAliases.length); + expect(auditLogger.log).toHaveBeenCalledTimes(legacyUrlAliases.length); + let i = 1; + for (const alias of legacyUrlAliases) { + const legacyObjectId = `${alias.targetSpace}:${alias.targetType}:${alias.sourceId}`; + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: undefined, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: 'legacy-url-alias', id: legacyObjectId }, + }, + message: `User is updating legacy-url-alias [id=${legacyObjectId}]`, + }); + } + }); + + test(`throws when unauthorized`, async () => { + const { securityExtension, checkPrivileges } = setup(); + setupSimpleCheckPrivsMockResolve(checkPrivileges, 'a', 'bulk_update', false); + + await expect( + securityExtension.authorizeDisableLegacyUrlAliases(legacyUrlAliases) + ).rejects.toThrow(`Unable to bulk_update a,b,c`); + expect(enforceAuthorizationSpy).toHaveBeenCalledTimes(1); + }); + + test(`adds an audit event per object when unauthorized`, async () => { + const { securityExtension, checkPrivileges, auditLogger } = setup(); + checkPrivileges.mockResolvedValue({ + hasAllRequested: false, + privileges: { + kibana: [ + { privilege: 'mock-saved_object:a/share_to_space', authorized: false }, + { privilege: 'login:', authorized: true }, + ], + }, + } as CheckPrivilegesResponse); + + await expect( + securityExtension.authorizeDisableLegacyUrlAliases(legacyUrlAliases) + ).rejects.toThrow(`Unable to bulk_update a,b,c`); + + expect(auditHelperSpy).toHaveBeenCalledTimes(1); + expect(addAuditEventSpy).toHaveBeenCalledTimes(legacyUrlAliases.length); + expect(auditLogger.log).toHaveBeenCalledTimes(legacyUrlAliases.length); + let i = 1; + for (const alias of legacyUrlAliases) { + const legacyObjectId = `${alias.targetSpace}:${alias.targetType}:${alias.sourceId}`; + expect(auditLogger.log).toHaveBeenNthCalledWith(i++, { + error: { + code: 'Error', + message: 'Unable to bulk_update a,b,c', + }, + event: { + action: AuditAction.UPDATE, + category: ['database'], + outcome: 'failure', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: undefined, + saved_object: { type: 'legacy-url-alias', id: legacyObjectId }, + }, + message: `Failed attempt to update legacy-url-alias [id=${legacyObjectId}]`, + }); + } + }); +}); + +describe(`#auditObjectsForSpaceDeletion`, () => { + const spaceId = 'x'; + + const objects: Array> = [ + { + id: '1', + namespaces: ['*'], + type: 'dashboard', + score: 1, + attributes: undefined, + references: [], + }, + { + id: '2', + namespaces: ['x'], + type: 'dashboard', + score: 1, + attributes: undefined, + references: [], + }, + { + id: '3', + namespaces: ['default', 'x'], + type: 'dashboard', + score: 1, + attributes: undefined, + references: [], + }, + ]; + + beforeEach(() => { + checkAuthorizationSpy.mockClear(); + enforceAuthorizationSpy.mockClear(); + redactNamespacesSpy.mockClear(); + authorizeSpy.mockClear(); + auditHelperSpy.mockClear(); + addAuditEventSpy.mockClear(); + }); + + test(`adds audit event for each object without '*' in namespaces`, async () => { + const { securityExtension, auditLogger } = setup(); + securityExtension.auditObjectsForSpaceDeletion(spaceId, objects); + + expect(auditHelperSpy).not.toHaveBeenCalled(); // The helper is not called, the addAudit method is called directly + expect(addAuditEventSpy).toHaveBeenCalledTimes(objects.length - 1); + expect(auditLogger.log).toHaveBeenCalledTimes(objects.length - 1); + const i = 0; + for (const obj of objects) { + if (i === 0) continue; // The first object namespaces includes '*', so there will not be an audit for it + + expect(auditLogger.log).toHaveBeenNthCalledWith(i, { + error: undefined, + event: { + action: AuditAction.UPDATE_OBJECTS_SPACES, + category: ['database'], + outcome: 'unknown', + type: ['change'], + }, + kibana: { + add_to_spaces: undefined, + delete_from_spaces: obj.namespaces!.length > 1 ? obj.namespaces : undefined, + saved_object: undefined, + }, + message: `User is updating spaces of dashboard [id=${obj.id}]`, + }); + } }); }); diff --git a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts index e464fbe8e4478..a540b96df3517 100644 --- a/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts +++ b/x-pack/plugins/security/server/saved_objects/saved_objects_security_extension.ts @@ -5,19 +5,43 @@ * 2.0. */ +import type { + SavedObjectReferenceWithContext, + SavedObjectsFindResult, + SavedObjectsResolveResponse, +} from '@kbn/core-saved-objects-api-server'; import type { SavedObjectsClient } from '@kbn/core-saved-objects-api-server-internal'; +import { isBulkResolveError } from '@kbn/core-saved-objects-api-server-internal/src/lib/internal_bulk_resolve'; +import { LEGACY_URL_ALIAS_TYPE } from '@kbn/core-saved-objects-base-server-internal'; +import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import type { - AddAuditEventParams, AuthorizationTypeEntry, AuthorizationTypeMap, - CheckAuthorizationParams, + AuthorizeAndRedactInternalBulkResolveParams, + AuthorizeAndRedactMultiNamespaceReferencesParams, + AuthorizeBulkCreateParams, + AuthorizeBulkDeleteParams, + AuthorizeBulkGetParams, + AuthorizeBulkUpdateParams, + AuthorizeCheckConflictsParams, + AuthorizeCreateParams, + AuthorizeDeleteParams, + AuthorizeFindParams, + AuthorizeGetParams, + AuthorizeOpenPointInTimeParams, + AuthorizeUpdateParams, + AuthorizeUpdateSpacesParams, + BulkResolveError, CheckAuthorizationResult, - EnforceAuthorizationParams, + GetFindRedactTypeMapParams, ISavedObjectsSecurityExtension, - PerformAuthorizationParams, RedactNamespacesParams, SavedObject, } from '@kbn/core-saved-objects-server'; +import type { AuthorizeObject } from '@kbn/core-saved-objects-server/src/extensions/security'; +import { ALL_NAMESPACES_STRING, SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; +import type { EcsEvent } from '@kbn/ecs'; import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants'; import type { AuditLogger } from '../audit'; @@ -33,17 +57,333 @@ interface Params { checkPrivileges: CheckSavedObjectsPrivileges; } +/** + * The SecurityAction enumeration contains values for all valid shared object + * security actions. The string for each value correlates to the ES operation. + */ +export enum SecurityAction { + CHECK_CONFLICTS, + CLOSE_POINT_IN_TIME, + COLLECT_MULTINAMESPACE_REFERENCES, + COLLECT_MULTINAMESPACE_REFERENCES_UPDATE_SPACES, + CREATE, + BULK_CREATE, + DELETE, + BULK_DELETE, + FIND, + GET, + BULK_GET, + INTERNAL_BULK_RESOLVE, + OPEN_POINT_IN_TIME, + REMOVE_REFERENCES, + UPDATE, + BULK_UPDATE, + UPDATE_OBJECTS_SPACES, +} + +/** + * The AuditAction enumeration contains values for all + * valid audit actions for use in AddAuditEventParams. + */ +export enum AuditAction { + CREATE = 'saved_object_create', + GET = 'saved_object_get', + RESOLVE = 'saved_object_resolve', + UPDATE = 'saved_object_update', + DELETE = 'saved_object_delete', + FIND = 'saved_object_find', + REMOVE_REFERENCES = 'saved_object_remove_references', + OPEN_POINT_IN_TIME = 'saved_object_open_point_in_time', + CLOSE_POINT_IN_TIME = 'saved_object_close_point_in_time', + COLLECT_MULTINAMESPACE_REFERENCES = 'saved_object_collect_multinamespace_references', // this is separate from 'saved_object_get' because the user is only accessing an object's metadata + UPDATE_OBJECTS_SPACES = 'saved_object_update_objects_spaces', // this is separate from 'saved_object_update' because the user is only updating an object's metadata +} + +/** + * The AddAuditEventParams interface contains settings for adding + * audit events via the ISavedObjectsSecurityExtension. This is + * used only for the private addAuditEvent method. + */ +export interface AddAuditEventParams { + /** The relevant action */ + action: AuditAction; + /** + * The outcome of the operation + * 'failure' | 'success' | 'unknown' + */ + outcome?: EcsEvent['outcome']; + /** + * Relevant saved object information + * object containing type & id strings + */ + savedObject?: { type: string; id: string }; + /** + * Array of spaces being added. For + * UPDATE_OBJECTS_SPACES action only + */ + addToSpaces?: readonly string[]; + /** + * Array of spaces being removed. For + * UPDATE_OBJECTS_SPACES action only + */ + deleteFromSpaces?: readonly string[]; + /** + * Relevant error information to add to + * the audit event + */ + error?: Error; +} + +/** + * The InternalAuthorizeOptions interface contains basic options + * for internal authorize methods of the ISavedObjectsSecurityExtension. + */ +interface InternalAuthorizeOptions { + /** + * Whether or not to force the use of the bulk action for the authorization. + * By default this will be based on the number of objects passed to the + * authorize method. + */ + forceBulkAction: boolean; +} + +/** + * The InternalAuthorizeParams interface contains settings for checking + * & enforcing authorization via the ISavedObjectsSecurityExtension. This + * is used only for the private authorize method. + */ +interface InternalAuthorizeParams { + /** A set of actions to check */ + actions: Set; + /** A set of types to check */ + types: Set; + /** A set of spaces to check */ + spaces: Set; + /** + * A map of types (key) to spaces (value) that will be affected by the action(s). + * If undefined, enforce will be bypassed. + */ + enforceMap?: Map>; + /** Options for authorization*/ + options?: { + /** allowGlobalResource - whether or not to allow global resources, false if options are undefined */ + allowGlobalResource?: boolean; + }; + /** auditOptions - options for audit logging */ + auditOptions?: AuditOptions | UpdateSpacesAuditOptions; +} + +/** + * The EnforceAuthorizationParams interface contains settings for + * enforcing a single action via the ISavedObjectsSecurityExtension. + * This is used only for the private enforceAuthorization method. + */ +interface EnforceAuthorizationParams { + /** A map of types to spaces that will be affected by the action */ + typesAndSpaces: Map>; + /** The relevant security action (create, update, etc.) */ + action: SecurityAction; + /** + * The authorization map from CheckAuthorizationResult: a + * map of type to record of action/AuthorizationTypeEntry + * (spaces/globallyAuthz'd) + */ + typeMap: AuthorizationTypeMap; + /** auditOptions - options for audit logging */ + auditOptions?: AuditOptions | UpdateSpacesAuditOptions; +} + +/** + * The AuditHelperParams interface contains parameters to log audit events + * within the ISavedObjectsSecurityExtension. + */ +interface AuditHelperParams { + /** The audit action to log */ + action: AuditAction; + /** The objects applicable to the action */ + objects?: Array<{ type: string; id: string }>; + /** Whether or not to use success as the non-failure outcome. Default is 'unknown' */ + useSuccessOutcome?: boolean; + /** + * The spaces in which to add the objects. + * Used only with the AuditAction.UPDATE_OBJECTS_SPACES. + * Default is none + */ + addToSpaces?: string[]; + /** + * The spaces from which to remove the objects. + * Used only with the AuditAction.UPDATE_OBJECTS_SPACES. + * Default is none + */ + deleteFromSpaces?: string[]; + /** Error information produced by the action */ + error?: Error; +} + +/** + * The AuditOptions interface contains optional settings for audit + * logging within the ISavedObjectsSecurityExtension. + */ +interface AuditOptions { + /** + * An array of applicable objects for the authorization action + * If undefined or empty, general auditing will occur (one log/action) + */ + objects?: Array<{ type: string; id: string }>; + /** + * Whether or not to bypass audit logging on authz success, authz failure, always, or never. Default never. + */ + bypass?: 'never' | 'on_success' | 'on_failure' | 'always'; + /** If authz success should be logged as 'success'. Default false */ + useSuccessOutcome?: boolean; +} + +/** + * The UpdateSpacesAuditOptions interface contains optional settings for + * auditing the UPDATE_OBJECTS_SPACES action. + */ +interface UpdateSpacesAuditOptions extends AuditOptions { + /** + * An array of spaces which to add the objects (used in updateObjectsSpaces) + */ + addToSpaces?: string[]; + /** + * An array of spaces from which to remove the objects (used in updateObjectsSpaces) + */ + deleteFromSpaces?: string[]; +} + +/** + * The CheckAuthorizationParams interface contains settings for checking + * authorization via the ISavedObjectsSecurityExtension. + */ +interface CheckAuthorizationParams { + /** A set of types to check */ + types: Set; + /** A set of spaces to check */ + spaces: Set; + /** An set of actions to check */ + actions: Set; + /** Authorization options */ + options?: { + /** + * allowGlobalResource - whether or not to allow global resources, false if options are undefined + */ + allowGlobalResource: boolean; + }; +} + export class SavedObjectsSecurityExtension implements ISavedObjectsSecurityExtension { private readonly actions: Actions; private readonly auditLogger: AuditLogger; private readonly errors: SavedObjectsClient['errors']; private readonly checkPrivilegesFunc: CheckSavedObjectsPrivileges; + private readonly actionMap: Map< + SecurityAction, + { authzAction?: string; auditAction?: AuditAction } + >; constructor({ actions, auditLogger, errors, checkPrivileges }: Params) { this.actions = actions; this.auditLogger = auditLogger; this.errors = errors; this.checkPrivilegesFunc = checkPrivileges; + + // This comment block is a quick reference for the action map, which maps authorization actions + // and audit actions to a "security action" as used by the authorization methods. + // Security Action ES AUTH ACTION AUDIT ACTION + // ----------------------------------------------------------------------------------------- + // Check Conflicts 'bulk_create' N/A + // Close PIT N/A AuditAction.CLOSE_POINT_IN_TIME + // Collect References 'bulk_get' AuditAction.COLLECT_MULTINAMESPACE_REFERENCES + // Collect Refs For Updating Spaces 'share_to_space' AuditAction.COLLECT_MULTINAMESPACE_REFERENCES + // Create 'create' AuditAction.CREATE + // Bulk Create 'bulk_create' AuditAction.CREATE + // Delete 'delete' AuditAction.DELETE + // Bulk Delete 'bulk_delete' AuditAction.DELETE + // Find 'find' AuditAction.FIND + // Get 'get' AuditAction.GET + // Bulk Get 'bulk_get' AuditAction.GET + // Internal Bulk Resolve 'bulk_get' AuditAction.RESOLVE + // Open PIT 'open_point_in_time' AuditAction.OPEN_POINT_IN_TIME + // Remove References 'delete' AuditAction.REMOVE_REFERENCES + // Update 'update' AuditAction.UPDATE + // Bulk Update 'bulk_update' AuditAction.UPDATE + // Update Objects Spaces 'share_to_space' AuditAction.UPDATE_OBJECTS_SPACES + this.actionMap = new Map([ + [SecurityAction.CHECK_CONFLICTS, { authzAction: 'bulk_create', auditAction: undefined }], + [ + SecurityAction.CLOSE_POINT_IN_TIME, + { authzAction: undefined, auditAction: AuditAction.CLOSE_POINT_IN_TIME }, + ], + [ + SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES, + { authzAction: 'bulk_get', auditAction: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES }, + ], + [ + SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES_UPDATE_SPACES, + { + authzAction: 'share_to_space', + auditAction: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, + }, + ], + [SecurityAction.CREATE, { authzAction: 'create', auditAction: AuditAction.CREATE }], + [SecurityAction.BULK_CREATE, { authzAction: 'bulk_create', auditAction: AuditAction.CREATE }], + [SecurityAction.DELETE, { authzAction: 'delete', auditAction: AuditAction.DELETE }], + [SecurityAction.BULK_DELETE, { authzAction: 'bulk_delete', auditAction: AuditAction.DELETE }], + [SecurityAction.FIND, { authzAction: 'find', auditAction: AuditAction.FIND }], + [SecurityAction.GET, { authzAction: 'get', auditAction: AuditAction.GET }], + [SecurityAction.BULK_GET, { authzAction: 'bulk_get', auditAction: AuditAction.GET }], + [ + SecurityAction.INTERNAL_BULK_RESOLVE, + { authzAction: 'bulk_get', auditAction: AuditAction.RESOLVE }, + ], + [ + SecurityAction.OPEN_POINT_IN_TIME, + { authzAction: 'open_point_in_time', auditAction: AuditAction.OPEN_POINT_IN_TIME }, + ], + [ + SecurityAction.REMOVE_REFERENCES, + { authzAction: 'delete', auditAction: AuditAction.REMOVE_REFERENCES }, + ], + [SecurityAction.UPDATE, { authzAction: 'update', auditAction: AuditAction.UPDATE }], + [SecurityAction.BULK_UPDATE, { authzAction: 'bulk_update', auditAction: AuditAction.UPDATE }], + [ + SecurityAction.UPDATE_OBJECTS_SPACES, + { authzAction: 'share_to_space', auditAction: AuditAction.UPDATE_OBJECTS_SPACES }, + ], + ]); + } + + private assertObjectsArrayNotEmpty(objects: AuthorizeObject[], action: SecurityAction) { + if (objects.length === 0) { + throw new Error( + `No objects specified for ${ + this.actionMap.get(action)?.authzAction ?? 'unknown' + } authorization` + ); + } + } + + private translateActions( + securityActions: Set + ): { authzActions: Set; auditActions: Set } { + const authzActions = new Set(); + const auditActions = new Set(); + for (const secAction of securityActions) { + const { authzAction, auditAction } = this.decodeSecurityAction(secAction); + if (authzAction) authzActions.add(authzAction as A); + if (auditAction) auditActions.add(auditAction as AuditAction); + } + return { authzActions, auditActions }; + } + + private decodeSecurityAction(securityAction: SecurityAction): { + authzAction: string | undefined; + auditAction: AuditAction | undefined; + } { + const { authzAction, auditAction } = this.actionMap.get(securityAction)!; + return { authzAction, auditAction }; } private async checkAuthorization( @@ -83,7 +423,6 @@ export class SavedObjectsSecurityExtension implements ISavedObjectsSecurityExten if (missingPrivilegesAtResource) { return acc; } - let objTypes: string[]; let action: A; if (privilege === this.actions.login) { @@ -139,33 +478,107 @@ export class SavedObjectsSecurityExtension implements ISavedObjectsSecurityExten return { typeMap, status: 'unauthorized' }; } - enforceAuthorization(params: EnforceAuthorizationParams): void { - const { typesAndSpaces, action, typeMap, auditCallback } = params; + private auditHelper(params: AuditHelperParams) { + const { action, useSuccessOutcome, objects, error, addToSpaces, deleteFromSpaces } = params; + + // If there are no objects, we at least want to add a single audit log for the action + const toAudit = !!objects && objects?.length > 0 ? objects : ([undefined] as undefined[]); + for (const obj of toAudit) { + this.addAuditEvent({ + action, + ...(!!obj && { savedObject: { type: obj.type, id: obj.id } }), + error, + // By default, if authorization was a success the outcome is 'unknown' because the operation has not occurred yet + // The GET action is one of the few exceptions to this, and hence it passes true to useSuccessOutcome + ...(!error && { outcome: useSuccessOutcome ? 'success' : 'unknown' }), + addToSpaces, + deleteFromSpaces, + }); + } + } + + /** + * The enforce method uses the result of an authorization check authorization map) and a map + * of types to spaces (type map) to determine if the action is authorized for all types and spaces + * within the type map. If unauthorized for any type this method will throw. + */ + private enforceAuthorization(params: EnforceAuthorizationParams): void { + const { typesAndSpaces, action, typeMap, auditOptions } = params; + const { + objects: auditObjects, + bypass = 'never', // default for bypass + useSuccessOutcome, + addToSpaces, + deleteFromSpaces, + } = (auditOptions as UpdateSpacesAuditOptions) ?? {}; + + const { authzAction, auditAction } = this.decodeSecurityAction(action); + const unauthorizedTypes = new Set(); - for (const [type, spaces] of typesAndSpaces) { - const spacesArray = [...spaces]; - if (!isAuthorizedInAllSpaces(type, action, spacesArray, typeMap)) { - unauthorizedTypes.add(type); + + if (authzAction) { + for (const [type, spaces] of typesAndSpaces) { + const spacesArray = [...spaces]; + if (!isAuthorizedInAllSpaces(type, authzAction as A, spacesArray, typeMap)) { + unauthorizedTypes.add(type); + } } } if (unauthorizedTypes.size > 0) { const targetTypes = [...unauthorizedTypes].sort().join(','); - const msg = `Unable to ${action} ${targetTypes}`; + const msg = `Unable to ${authzAction} ${targetTypes}`; const error = this.errors.decorateForbiddenError(new Error(msg)); - auditCallback?.(error); + if (auditAction && bypass !== 'always' && bypass !== 'on_failure') { + this.auditHelper({ + action: auditAction, + objects: auditObjects, + useSuccessOutcome, + addToSpaces, + deleteFromSpaces, + error, + }); + } throw error; } - auditCallback?.(); + + if (auditAction && bypass !== 'always' && bypass !== 'on_success') { + this.auditHelper({ action: auditAction, objects: auditObjects, useSuccessOutcome }); + } } - async performAuthorization( - params: PerformAuthorizationParams - ): Promise> { + /** + * The authorize method is the central method for authorization within the extension. It handles + * checking and enforcing authorization, and passing audit parameters down to the enforce method. + * + * If an enforce map is not provided, this method will NOT enforce authorization nor audit the action. + * If an enforce map is provided and the action is unauthorized for any type in any space mapped for + * that type, this method will throw (because the enforce method will throw). + * + * This method not marked as private, but not exposed via the interface + * This allows us to test it thoroughly in the unit test suite, but keep it from being exposed to consumers. + * @param params actions, types, and spaces to check, the enforce map (types to enforce in which spaces), options, and audit options + * @returns CheckAuthorizationResult - the result from the authorizations check + */ + async authorize( + params: InternalAuthorizeParams + ): Promise> { + if (params.actions.size === 0) { + throw new Error('No actions specified for authorization'); + } + if (params.types.size === 0) { + throw new Error('No types specified for authorization'); + } + if (params.spaces.size === 0) { + throw new Error('No spaces specified for authorization'); + } + + const { authzActions } = this.translateActions(params.actions); + const checkResult: CheckAuthorizationResult = await this.checkAuthorization({ types: params.types, spaces: params.spaces, - actions: params.actions, + actions: authzActions, options: { allowGlobalResource: params.options?.allowGlobalResource === true }, }); @@ -176,7 +589,7 @@ export class SavedObjectsSecurityExtension implements ISavedObjectsSecurityExten typesAndSpaces, action, typeMap: checkResult.typeMap, - auditCallback: params.auditCallback, + auditOptions: params.auditOptions, }); }); } @@ -184,13 +597,24 @@ export class SavedObjectsSecurityExtension implements ISavedObjectsSecurityExten return checkResult; } - addAuditEvent(params: AddAuditEventParams): void { + private addAuditEvent(params: AddAuditEventParams): void { if (this.auditLogger.enabled) { const auditEvent = savedObjectEvent(params); this.auditLogger.log(auditEvent); } } + private async checkPrivileges( + actions: string | string[], + namespaceOrNamespaces?: string | Array + ) { + try { + return await this.checkPrivilegesFunc(actions, namespaceOrNamespaces); + } catch (error) { + throw this.errors.decorateGeneralError(error, error.body && error.body.reason); + } + } + redactNamespaces(params: RedactNamespacesParams): SavedObject { const { savedObject, typeMap } = params; const loginAction = this.actions.login as A; // This typeMap came from the `checkAuthorization` function, which always checks privileges for the "login" action (in addition to what the consumer requested) @@ -208,15 +632,725 @@ export class SavedObjectsSecurityExtension implements ISavedObjectsSecurityExten return { ...savedObject, namespaces: redactedSpaces }; } - private async checkPrivileges( - actions: string | string[], - namespaceOrNamespaces?: string | Array - ) { - try { - return await this.checkPrivilegesFunc(actions, namespaceOrNamespaces); - } catch (error) { - throw this.errors.decorateGeneralError(error, error.body && error.body.reason); + async authorizeCreate( + params: AuthorizeCreateParams + ): Promise> { + return this.internalAuthorizeCreate({ + namespace: params.namespace, + objects: [params.object], + }); + } + + async authorizeBulkCreate( + params: AuthorizeBulkCreateParams + ): Promise> { + return this.internalAuthorizeCreate(params, { forceBulkAction: true }); + } + + private async internalAuthorizeCreate( + params: AuthorizeBulkCreateParams, + options?: InternalAuthorizeOptions + ): Promise> { + const namespaceString = SavedObjectsUtils.namespaceIdToString(params.namespace); + const { objects } = params; + + const action = + options?.forceBulkAction || objects.length > 1 + ? SecurityAction.BULK_CREATE + : SecurityAction.CREATE; + + this.assertObjectsArrayNotEmpty(objects, action); + + const enforceMap = new Map>(); + const spacesToAuthorize = new Set([namespaceString]); // Always check authZ for the active space + + // If a user tries to create an object with `initialNamespaces: ['*']`, they need to have 'create' privileges for the Global Resource + // (e.g., All privileges for All Spaces). + // Inversely, if a user tries to overwrite an object that already exists in '*', they don't need to 'create' privileges for the Global + // Resource, so in that case we have to filter out that string from spacesToAuthorize (because `allowGlobalResource: true` is used + // below.) + for (const obj of objects) { + const spacesToEnforce = enforceMap.get(obj.type) ?? new Set([namespaceString]); // Always enforce authZ for the active space + for (const space of obj.initialNamespaces ?? []) { + spacesToEnforce.add(space); + spacesToAuthorize.add(space); + } + enforceMap.set(obj.type, spacesToEnforce); + + for (const space of obj.existingNamespaces) { + // Don't accidentally check for global privileges when the object exists in '*' + if (space !== ALL_NAMESPACES_STRING) { + spacesToAuthorize.add(space); // existing namespaces are included so we can later redact if necessary + } + } + } + + const authorizationResult = await this.authorize({ + actions: new Set([action]), + types: new Set(enforceMap.keys()), + spaces: spacesToAuthorize, + enforceMap, + options: { allowGlobalResource: true }, + auditOptions: { objects }, + }); + + return authorizationResult; + } + + async authorizeUpdate( + params: AuthorizeUpdateParams + ): Promise> { + return this.internalAuthorizeUpdate({ + namespace: params.namespace, + objects: [params.object], + }); + } + + async authorizeBulkUpdate( + params: AuthorizeBulkUpdateParams + ): Promise> { + return this.internalAuthorizeUpdate(params, { forceBulkAction: true }); + } + + private async internalAuthorizeUpdate( + params: AuthorizeBulkUpdateParams, + options?: InternalAuthorizeOptions + ): Promise> { + const namespaceString = SavedObjectsUtils.namespaceIdToString(params.namespace); + const { objects } = params; + + const action = + options?.forceBulkAction || objects.length > 1 + ? SecurityAction.BULK_UPDATE + : SecurityAction.UPDATE; + + this.assertObjectsArrayNotEmpty(objects, action); + + const enforceMap = new Map>(); + const spacesToAuthorize = new Set([namespaceString]); // Always check authZ for the active space + + for (const obj of objects) { + const { + type, + objectNamespace: objectNamespace, + existingNamespaces: existingNamespaces, + } = obj; + const objectNamespaceString = objectNamespace ?? namespaceString; + + const spacesToEnforce = enforceMap.get(type) ?? new Set([namespaceString]); // Always enforce authZ for the active space + spacesToEnforce.add(objectNamespaceString); + enforceMap.set(type, spacesToEnforce); + spacesToAuthorize.add(objectNamespaceString); + + for (const space of existingNamespaces) { + spacesToAuthorize.add(space); // existing namespaces are included so we can later redact if necessary + } + } + + const authorizationResult = await this.authorize({ + actions: new Set([action]), + types: new Set(enforceMap.keys()), + spaces: spacesToAuthorize, + enforceMap, + auditOptions: { objects }, + }); + + return authorizationResult; + } + + async authorizeDelete( + params: AuthorizeDeleteParams + ): Promise> { + return this.internalAuthorizeDelete({ + namespace: params.namespace, + // delete params does not contain existingNamespaces because authz + // occurs prior to the preflight check. This is ok because we are + // only concerned with enforcing the current space. + objects: [{ ...params.object, existingNamespaces: [] }], + }); + } + + async authorizeBulkDelete( + params: AuthorizeBulkDeleteParams + ): Promise> { + return this.internalAuthorizeDelete(params, { forceBulkAction: true }); + } + + private async internalAuthorizeDelete( + params: AuthorizeBulkDeleteParams, + options?: InternalAuthorizeOptions + ): Promise> { + const namespaceString = SavedObjectsUtils.namespaceIdToString(params.namespace); + const { objects } = params; + const enforceMap = new Map>(); + const spacesToAuthorize = new Set([namespaceString]); // Always check authZ for the active space + + const action = + options?.forceBulkAction || objects.length > 1 + ? SecurityAction.BULK_DELETE + : SecurityAction.DELETE; + + this.assertObjectsArrayNotEmpty(objects, action); + + for (const obj of objects) { + const { type } = obj; + const spacesToEnforce = enforceMap.get(type) ?? new Set([namespaceString]); // Always enforce authZ for the active space + enforceMap.set(type, spacesToEnforce); + for (const space of obj.existingNamespaces) { + spacesToAuthorize.add(space); // existing namespaces are authorized but not enforced + } + } + + return this.authorize({ + actions: new Set([action]), + types: new Set(enforceMap.keys()), + spaces: spacesToAuthorize, + enforceMap, + auditOptions: { + objects, + }, + }); + } + + async authorizeGet( + params: AuthorizeGetParams + ): Promise> { + const { namespace, object, objectNotFound } = params; + const spacesToEnforce = new Set([SavedObjectsUtils.namespaceIdToString(namespace)]); // Always check/enforce authZ for the active space + const existingNamespaces = object.existingNamespaces; + + return await this.authorize({ + actions: new Set([SecurityAction.GET]), + types: new Set([object.type]), + spaces: new Set([...spacesToEnforce, ...existingNamespaces]), // existing namespaces are included so we can later redact if necessary + enforceMap: new Map([[object.type, spacesToEnforce]]), + auditOptions: { objects: [object], bypass: objectNotFound ? 'on_success' : 'never' }, // Do not audit on success if the object was not found + }); + } + + async authorizeBulkGet( + params: AuthorizeBulkGetParams + ): Promise> { + const action = SecurityAction.BULK_GET; + const namespace = SavedObjectsUtils.namespaceIdToString(params.namespace); + const { objects } = params; + this.assertObjectsArrayNotEmpty(objects, action); + + const successAuditObjects = new Array<{ type: string; id: string }>(); + const enforceMap = new Map>(); + const spacesToAuthorize = new Set([namespace]); // Always check authZ for the active space + + for (const obj of objects) { + const spacesToEnforce = enforceMap.get(obj.type) ?? new Set([namespace]); // Always enforce authZ for the active space + + // Object namespaces are passed into the repo's bulkGet method per object + for (const space of obj.objectNamespaces ?? []) { + spacesToEnforce.add(space); + enforceMap.set(obj.type, spacesToEnforce); + spacesToAuthorize.add(space); + } + + // Existing namespaces are populated fom the bulkGet response docs + for (const space of obj.existingNamespaces) { + spacesToAuthorize.add(space); // existing namespaces are included so we can later redact if necessary + } + + // We only log success events for objects that were actually found (and are being returned to the user) + // If enforce fails, we audit for all objects + if (!obj.error) { + successAuditObjects.push(obj); + } + } + + const authorizationResult = await this.authorize({ + actions: new Set([action]), + types: new Set(enforceMap.keys()), + spaces: spacesToAuthorize, + enforceMap, + auditOptions: { + objects, + useSuccessOutcome: true, + bypass: 'on_success', // We will override the success case below + }, + }); + + // if we made it here, enforce was a success, so let's audit... + const { auditAction } = this.decodeSecurityAction(SecurityAction.BULK_GET); + if (auditAction) { + this.auditHelper({ + action: auditAction, + objects: successAuditObjects.length ? successAuditObjects : undefined, + useSuccessOutcome: true, + }); + } + + return authorizationResult; + } + + async authorizeCheckConflicts( + params: AuthorizeCheckConflictsParams + ): Promise> { + const action = SecurityAction.CHECK_CONFLICTS; + const { namespace, objects } = params; + this.assertObjectsArrayNotEmpty(objects, action); + + const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); + const typesAndSpaces = new Map>(); + for (const obj of params.objects) { + typesAndSpaces.set(obj.type, new Set([namespaceString])); // Always enforce authZ for the active space + } + + return this.authorize({ + actions: new Set([SecurityAction.CHECK_CONFLICTS]), + types: new Set(typesAndSpaces.keys()), + spaces: new Set([namespaceString]), // Always check authZ for the active space + enforceMap: typesAndSpaces, + // auditing is intentionally bypassed, this function in the previous Security SOC wrapper implementation + // did not have audit logging. This is primarily because it is only used by Kibana and is not exposed in a + // public HTTP API + auditOptions: { bypass: 'always' }, + }); + } + + async authorizeRemoveReferences( + params: AuthorizeDeleteParams + ): Promise> { + // TODO: Improve authorization and auditing (https://github.com/elastic/kibana/issues/135259) + const { namespace, object } = params; + const spaces = new Set([SavedObjectsUtils.namespaceIdToString(namespace)]); // Always check/enforce authZ for the active space + return this.authorize({ + actions: new Set([SecurityAction.REMOVE_REFERENCES]), + types: new Set([object.type]), + spaces, + enforceMap: new Map([[object.type, spaces]]), + auditOptions: { objects: [object] }, + }); + } + + async authorizeOpenPointInTime( + params: AuthorizeOpenPointInTimeParams + ): Promise> { + const { namespaces, types } = params; + + const preAuthorizationResult = await this.authorize({ + actions: new Set([SecurityAction.OPEN_POINT_IN_TIME]), + types, + spaces: namespaces, + // No need to bypass in audit options - enforce is completely bypassed (no enforce map) + }); + + if (preAuthorizationResult?.status === 'unauthorized') { + // If the user is unauthorized to find *anything* they requested, throw + this.addAuditEvent({ + action: AuditAction.OPEN_POINT_IN_TIME, + error: new Error('User is unauthorized for any requested types/spaces'), + // TODO: include object type(s) that were requested? + // requestedTypes: types, + // requestedSpaces: namespaces, + }); + throw SavedObjectsErrorHelpers.decorateForbiddenError(new Error('unauthorized')); + } + this.addAuditEvent({ + action: AuditAction.OPEN_POINT_IN_TIME, + outcome: 'unknown', + // TODO: include object type(s) that were requested? + // requestedTypes: types, + // requestedSpaces: namespaces, + }); + + return preAuthorizationResult; + } + + auditClosePointInTime() { + this.addAuditEvent({ + action: AuditAction.CLOSE_POINT_IN_TIME, + outcome: 'unknown', + }); + } + + async authorizeAndRedactMultiNamespaceReferences( + params: AuthorizeAndRedactMultiNamespaceReferencesParams + ): Promise { + const namespaceString = SavedObjectsUtils.namespaceIdToString(params.namespace); + const { objects, options = {} } = params; + if (objects.length === 0) return objects; + const { purpose } = options; + + // Check authorization based on all *found* object types / spaces + const typesToAuthorize = new Set(); + const spacesToAuthorize = new Set([namespaceString]); + const addSpacesToAuthorize = (spaces: string[] = []) => { + for (const space of spaces) spacesToAuthorize.add(space); + }; + for (const obj of objects) { + typesToAuthorize.add(obj.type); + addSpacesToAuthorize(obj.spaces); + addSpacesToAuthorize(obj.spacesWithMatchingAliases); + addSpacesToAuthorize(obj.spacesWithMatchingOrigins); + } + const action = + purpose === 'updateObjectsSpaces' + ? SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES_UPDATE_SPACES + : SecurityAction.COLLECT_MULTINAMESPACE_REFERENCES; + + // Enforce authorization based on all *requested* object types and the current space + const typesAndSpaces = objects.reduce( + (acc, { type }) => (acc.has(type) ? acc : acc.set(type, new Set([namespaceString]))), // Always enforce authZ for the active space + new Map>() + ); + + const { typeMap } = (await this.authorize({ + actions: new Set([action]), + types: typesToAuthorize, + spaces: spacesToAuthorize, + enforceMap: typesAndSpaces, + auditOptions: { bypass: 'on_success' }, // We will audit success results below, after redaction + })) ?? { typeMap: new Map() }; + + // Now, filter/redact the results. Most SOR functions just redact the `namespaces` field from each returned object. However, this function + // will actually filter the returned object graph itself. + // This is done in two steps: (1) objects which the user can't access *in this space* are filtered from the graph, and the + // graph is rearranged to avoid leaking information. (2) any spaces that the user can't access are redacted from each individual object. + // After we finish filtering, we can write audit events for each object that is going to be returned to the user. + const requestedObjectsSet = objects.reduce( + (acc, { type, id }) => acc.add(`${type}:${id}`), + new Set() + ); + const retrievedObjectsSet = objects.reduce( + (acc, { type, id }) => acc.add(`${type}:${id}`), + new Set() + ); + const traversedObjects = new Set(); + const filteredObjectsMap = new Map(); + const getIsAuthorizedForInboundReference = (inbound: { type: string; id: string }) => { + const found = filteredObjectsMap.get(`${inbound.type}:${inbound.id}`); + return found && !found.isMissing; // If true, this object can be linked back to one of the requested objects + }; + let objectsToProcess = [...objects]; + while (objectsToProcess.length > 0) { + const obj = objectsToProcess.shift()!; + const { type, id, spaces, inboundReferences } = obj; + const objKey = `${type}:${id}`; + traversedObjects.add(objKey); + // Is the user authorized to access this object in this space? + let isAuthorizedForObject = true; + try { + this.enforceAuthorization({ + typesAndSpaces: new Map([[type, new Set([namespaceString])]]), + action, + typeMap, + auditOptions: { bypass: 'always' }, // never audit here + }); + } catch (err) { + isAuthorizedForObject = false; + } + // Redact the inbound references so we don't leak any info about other objects that the user is not authorized to access + const redactedInboundReferences = inboundReferences.filter((inbound) => { + if (inbound.type === type && inbound.id === id) { + // circular reference, don't redact it + return true; + } + return getIsAuthorizedForInboundReference(inbound); + }); + // If the user is not authorized to access at least one inbound reference of this object, then we should omit this object. + const isAuthorizedForGraph = + requestedObjectsSet.has(objKey) || // If true, this is one of the requested objects, and we checked authorization above + redactedInboundReferences.some(getIsAuthorizedForInboundReference); + + if (isAuthorizedForObject && isAuthorizedForGraph) { + if (spaces.length) { + // Only generate success audit records for "non-empty results" with 1+ spaces + // ("empty result" means the object was a non-multi-namespace type, or hidden type, or not found) + this.addAuditEvent({ + action: AuditAction.COLLECT_MULTINAMESPACE_REFERENCES, + savedObject: { type, id }, + }); + } + filteredObjectsMap.set(objKey, obj); + } else if (!isAuthorizedForObject && isAuthorizedForGraph) { + filteredObjectsMap.set(objKey, { ...obj, spaces: [], isMissing: true }); + } else if (isAuthorizedForObject && !isAuthorizedForGraph) { + const hasUntraversedInboundReferences = inboundReferences.some( + (ref) => + !traversedObjects.has(`${ref.type}:${ref.id}`) && + retrievedObjectsSet.has(`${ref.type}:${ref.id}`) + ); + + if (hasUntraversedInboundReferences) { + // this object has inbound reference(s) that we haven't traversed yet; bump it to the back of the list + objectsToProcess = [...objectsToProcess, obj]; + } else { + // There should never be a missing inbound reference. + // If there is, then something has gone terribly wrong. + const missingInboundReference = inboundReferences.find( + (ref) => + !traversedObjects.has(`${ref.type}:${ref.id}`) && + !retrievedObjectsSet.has(`${ref.type}:${ref.id}`) + ); + + if (missingInboundReference) { + throw new Error( + `Unexpected inbound reference to "${missingInboundReference.type}:${missingInboundReference.id}"` + ); + } + } + } + } + + const filteredAndRedactedObjects = [ + ...filteredObjectsMap.values(), + ].map((obj) => { + const { + type, + id, + spaces, + spacesWithMatchingAliases, + spacesWithMatchingOrigins, + inboundReferences, + } = obj; + // Redact the inbound references so we don't leak any info about other objects that the user is not authorized to access + const redactedInboundReferences = inboundReferences.filter((inbound) => { + if (inbound.type === type && inbound.id === id) { + // circular reference, don't redact it + return true; + } + return getIsAuthorizedForInboundReference(inbound); + }); + + /** Simple wrapper for the `redactNamespaces` function that expects a saved object in its params. */ + const getRedactedSpaces = (spacesArray: string[] | undefined) => { + if (!spacesArray) return; + const savedObject = { type, namespaces: spacesArray } as SavedObject; // Other SavedObject attributes aren't required + const result = this.redactNamespaces({ savedObject, typeMap }); + return result.namespaces; + }; + const redactedSpaces = getRedactedSpaces(spaces)!; + const redactedSpacesWithMatchingAliases = getRedactedSpaces(spacesWithMatchingAliases); + const redactedSpacesWithMatchingOrigins = getRedactedSpaces(spacesWithMatchingOrigins); + return { + ...obj, + spaces: redactedSpaces, + ...(redactedSpacesWithMatchingAliases && { + spacesWithMatchingAliases: redactedSpacesWithMatchingAliases, + }), + ...(redactedSpacesWithMatchingOrigins && { + spacesWithMatchingOrigins: redactedSpacesWithMatchingOrigins, + }), + inboundReferences: redactedInboundReferences, + }; + }); + + return filteredAndRedactedObjects; + } + + async authorizeAndRedactInternalBulkResolve( + params: AuthorizeAndRedactInternalBulkResolveParams + ): Promise | BulkResolveError>> { + const { namespace, objects } = params; + const namespaceString = SavedObjectsUtils.namespaceIdToString(namespace); + const typesAndSpaces = new Map>(); + const spacesToAuthorize = new Set(); + const auditableObjects: Array<{ type: string; id: string }> = []; + + for (const result of objects) { + let auditableObject: { type: string; id: string } | undefined; + if (isBulkResolveError(result)) { + const { type, id, error } = result; + if (!SavedObjectsErrorHelpers.isBadRequestError(error)) { + // Only "not found" errors should show up as audit events (not "unsupported type" errors) + auditableObject = { type, id }; + } + } else { + const { type, id, namespaces = [] } = (result as SavedObjectsResolveResponse).saved_object; + auditableObject = { type, id }; + for (const space of namespaces) { + spacesToAuthorize.add(space); + } + } + if (auditableObject) { + auditableObjects.push(auditableObject); + const spacesToEnforce = + typesAndSpaces.get(auditableObject.type) ?? new Set([namespaceString]); // Always enforce authZ for the active space + spacesToEnforce.add(namespaceString); + typesAndSpaces.set(auditableObject.type, spacesToEnforce); + spacesToAuthorize.add(namespaceString); + } + } + + if (typesAndSpaces.size === 0) { + // We only had "unsupported type" errors, there are no types to check privileges for, just return early + return objects; } + + const { typeMap } = await this.authorize({ + actions: new Set([SecurityAction.INTERNAL_BULK_RESOLVE]), + types: new Set(typesAndSpaces.keys()), + spaces: spacesToAuthorize, + enforceMap: typesAndSpaces, + auditOptions: { useSuccessOutcome: true }, + }); + + return objects.map((result) => { + if (isBulkResolveError(result)) { + return result; + } + return { + ...result, + saved_object: this.redactNamespaces({ + typeMap, + savedObject: result.saved_object, + }), + }; + }); + } + + async authorizeUpdateSpaces( + params: AuthorizeUpdateSpacesParams + ): Promise> { + const action = SecurityAction.UPDATE_OBJECTS_SPACES; + const { objects, spacesToAdd, spacesToRemove } = params; + this.assertObjectsArrayNotEmpty(objects, action); + + const namespaceString = SavedObjectsUtils.namespaceIdToString(params.namespace); + const typesAndSpaces = new Map>(); + const spacesToAuthorize = new Set(); + for (const obj of objects) { + const { type, existingNamespaces } = obj; + + const spacesToEnforce = + typesAndSpaces.get(type) ?? new Set([...spacesToAdd, ...spacesToRemove, namespaceString]); // Always enforce authZ for the active space + typesAndSpaces.set(type, spacesToEnforce); + + for (const space of spacesToEnforce) { + spacesToAuthorize.add(space); + } + + for (const space of existingNamespaces) { + // Existing namespaces are included so we can later redact if necessary + // If this is a specific space, add it to the spaces we'll check privileges for (don't accidentally check for global privileges) + if (space === ALL_NAMESPACES_STRING) continue; + spacesToAuthorize.add(space); + } + } + + const addToSpaces = spacesToAdd.length ? spacesToAdd : undefined; + const deleteFromSpaces = spacesToRemove.length ? spacesToRemove : undefined; + return await this.authorize({ + // If a user tries to share/unshare an object to/from '*', they need to have 'share_to_space' privileges for the Global Resource + // (e.g., All privileges for All Spaces). + actions: new Set([SecurityAction.UPDATE_OBJECTS_SPACES]), + types: new Set(typesAndSpaces.keys()), + spaces: spacesToAuthorize, + enforceMap: typesAndSpaces, + options: { allowGlobalResource: true }, + auditOptions: { + objects, + addToSpaces, + deleteFromSpaces, + }, + }); + } + + async authorizeFind( + params: AuthorizeFindParams + ): Promise> { + const { types, namespaces } = params; + + const preAuthorizationResult = await this.authorize({ + actions: new Set([SecurityAction.FIND]), + types: new Set(types), + spaces: new Set(namespaces), + }); + if (preAuthorizationResult?.status === 'unauthorized') { + // If the user is unauthorized to find *anything* they requested, audit but don't throw + // This is one of the last remaining calls to addAuditEvent outside of the sec ext + this.addAuditEvent({ + action: AuditAction.FIND, + error: new Error(`User is unauthorized for any requested types/spaces`), + // TODO: Improve authorization and auditing (https://github.com/elastic/kibana/issues/135259) + // include object type(s) that were requested? + // requestedTypes: types, + // requestedSpaces: namespaces, + }); + } + return preAuthorizationResult; + } + + async getFindRedactTypeMap( + params: GetFindRedactTypeMapParams + ): Promise | undefined> { + const { previouslyCheckedNamespaces: authorizeNamespaces, objects } = params; + + const spacesToAuthorize = new Set(authorizeNamespaces); // only for namespace redaction + for (const { type, id, existingNamespaces } of objects) { + for (const space of existingNamespaces) { + spacesToAuthorize.add(space); + } + this.addAuditEvent({ + action: AuditAction.FIND, + savedObject: { type, id }, + }); + } + if (spacesToAuthorize.size > authorizeNamespaces.size) { + // If there are any namespaces in the object results that were not already checked during pre-authorization, we need *another* + // authorization check so we can correctly redact the object namespaces below. + const authorizationResult = await this.authorize({ + actions: new Set([SecurityAction.FIND]), + types: new Set(objects.map((obj) => obj.type)), + spaces: spacesToAuthorize, + }); + return authorizationResult.typeMap; + } + } + + async authorizeDisableLegacyUrlAliases(aliases: LegacyUrlAliasTarget[]) { + if (aliases.length === 0) throw new Error(`No aliases specified for authorization`); + + const [uniqueSpaces, typesAndSpaces] = aliases.reduce( + ([spaces, typesAndSpacesMap], { targetSpace, targetType }) => { + const spacesForType = typesAndSpacesMap.get(targetType) ?? new Set(); + return [ + spaces.add(targetSpace), + typesAndSpacesMap.set(targetType, spacesForType.add(targetSpace)), + ]; + }, + [new Set(), new Map>()] + ); + + await this.authorize({ + actions: new Set([SecurityAction.BULK_UPDATE]), + types: new Set(typesAndSpaces.keys()), + spaces: uniqueSpaces, + enforceMap: typesAndSpaces, + auditOptions: { + objects: aliases.map((alias) => { + return { + type: LEGACY_URL_ALIAS_TYPE, + id: `${alias.targetSpace}:${alias.targetType}:${alias.sourceId}`, + }; + }), + }, + }); + } + + auditObjectsForSpaceDeletion( + spaceId: string, + resultObjects: Array> + ) { + resultObjects.forEach((obj) => { + const { namespaces = [] } = obj; + const isOnlySpace = namespaces.length === 1; // We can always rely on the `namespaces` field having >=1 element + if (namespaces.includes(ALL_SPACES_ID) && !namespaces.includes(spaceId)) { + // This object exists in All Spaces and its `namespaces` field isn't going to change; there's nothing to audit + return; + } + this.addAuditEvent({ + action: isOnlySpace ? AuditAction.DELETE : AuditAction.UPDATE_OBJECTS_SPACES, + outcome: 'unknown', + savedObject: { type: obj.type, id: obj.id }, + ...(!isOnlySpace && { deleteFromSpaces: [spaceId] }), + }); + }); } } diff --git a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts index 085ab11076a57..8479b2f25f8c2 100644 --- a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.test.ts @@ -7,12 +7,10 @@ import { savedObjectsExtensionsMock } from '@kbn/core-saved-objects-api-server-mocks'; import type { ISavedObjectsSecurityExtension } from '@kbn/core-saved-objects-server'; -import { AuditAction } from '@kbn/core-saved-objects-server'; -import { setMapsAreEqual, setsAreEqual } from '@kbn/core-saved-objects-utils-server'; import type { EcsEvent, SavedObjectsFindResponse } from '@kbn/core/server'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { httpServerMock } from '@kbn/core/server/mocks'; -import type { GetAllSpacesPurpose, LegacyUrlAliasTarget, Space } from '@kbn/spaces-plugin/server'; +import type { GetAllSpacesPurpose, Space } from '@kbn/spaces-plugin/server'; import { spacesClientMock } from '@kbn/spaces-plugin/server/mocks'; import { deepFreeze } from '@kbn/std'; @@ -25,11 +23,7 @@ import type { } from '../authorization'; import { authorizationMock } from '../authorization/index.mock'; import type { CheckPrivilegesResponse } from '../authorization/types'; -import { - getAliasId, - LEGACY_URL_ALIAS_TYPE, - SecureSpacesClientWrapper, -} from './secure_spaces_client_wrapper'; +import { SecureSpacesClientWrapper } from './secure_spaces_client_wrapper'; interface Opts { securityEnabled?: boolean; @@ -670,7 +664,9 @@ describe('SecureSpacesClientWrapper', () => { const username = 'some_user'; const { wrapper, baseClient, authorization, auditLogger, request, securityExtension } = setup( - { securityEnabled: true } + { + securityEnabled: true, + } ); const checkPrivileges = jest.fn().mockResolvedValue({ @@ -698,18 +694,13 @@ describe('SecureSpacesClientWrapper', () => { type: 'space', id: space.id, }); - expect(securityExtension!.addAuditEvent).toHaveBeenCalledTimes(2); - expect(securityExtension!.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.DELETE, - outcome: 'unknown', - savedObject: { type: 'dashboard', id: '2' }, - }); - expect(securityExtension!.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE_OBJECTS_SPACES, - outcome: 'unknown', - savedObject: { type: 'dashboard', id: '3' }, - deleteFromSpaces: [space.id], - }); + + expect(securityExtension!.auditObjectsForSpaceDeletion).toHaveBeenCalledTimes(1); + expect(securityExtension!.auditObjectsForSpaceDeletion).toHaveBeenCalledWith(space.id, [ + { id: '1', namespaces: ['*'], type: 'dashboard' }, + { id: '2', namespaces: ['existing_space'], type: 'dashboard' }, + { id: '3', namespaces: ['default', 'existing_space'], type: 'dashboard' }, + ]); }); }); @@ -717,50 +708,15 @@ describe('SecureSpacesClientWrapper', () => { const alias1 = { targetSpace: 'space-1', targetType: 'type-1', sourceId: 'id' }; const alias2 = { targetSpace: 'space-2', targetType: 'type-2', sourceId: 'id' }; - function expectAuditEvents( - securityExtension: jest.Mocked, - aliases: LegacyUrlAliasTarget[], - { error }: { error: boolean } - ) { - aliases.forEach((alias) => { - expect(securityExtension!.addAuditEvent).toHaveBeenCalledWith({ - action: AuditAction.UPDATE, - savedObject: { type: LEGACY_URL_ALIAS_TYPE, id: getAliasId(alias) }, - ...(error ? { error: expect.anything() } : { outcome: 'unknown' }), - }); - }); - } - function expectAuthorizationCheck( securityExtension: jest.Mocked, aliases: Array<{ targetSpace: string; targetType: string }> ) { - expect(securityExtension.performAuthorization).toHaveBeenCalledTimes(1); - - const targetTypes = aliases.map((alias) => alias.targetType); - const targetSpaces = aliases.map((alias) => alias.targetSpace); - - const expectedActions = new Set(['bulk_update']); - const expectedSpaces = new Set(targetSpaces); - const expectedTypes = new Set(targetTypes); - const expectedEnforceMap = new Map>(); - aliases.forEach((alias) => { - expectedEnforceMap.set(alias.targetType, new Set([alias.targetSpace])); - }); - - const { - actions: actualActions, - spaces: actualSpaces, - types: actualTypes, - enforceMap: actualEnforceMap, - options: actualOptions, - } = securityExtension.performAuthorization.mock.calls[0][0]; - - expect(setsAreEqual(expectedActions, actualActions)).toBeTruthy(); - expect(setsAreEqual(expectedSpaces, actualSpaces)).toBeTruthy(); - expect(setsAreEqual(expectedTypes, actualTypes)).toBeTruthy(); - expect(setMapsAreEqual(expectedEnforceMap, actualEnforceMap)).toBeTruthy(); - expect(actualOptions).toBeUndefined(); + expect(securityExtension.authorizeDisableLegacyUrlAliases).toHaveBeenCalledTimes(1); + expect(securityExtension.authorizeDisableLegacyUrlAliases).toHaveBeenCalledWith([ + alias1, + alias2, + ]); } describe('when security is not enabled', () => { @@ -780,12 +736,12 @@ describe('SecureSpacesClientWrapper', () => { describe('when security is enabled', () => { const securityEnabled = true; - it('throws a forbidden error when unauthorized', async () => { + it('propagates decorated error when authorizeDisableLegacyUrlAliases throws', async () => { const { wrapper, baseClient, forbiddenError, securityExtension } = setup({ securityEnabled, }); - securityExtension!.performAuthorization.mockImplementation(() => { - throw new Error('Oh no!'); + securityExtension!.authorizeDisableLegacyUrlAliases.mockImplementation(() => { + throw forbiddenError; }); const aliases = [alias1, alias2]; await expect(() => wrapper.disableLegacyUrlAliases(aliases)).rejects.toThrow( @@ -793,23 +749,15 @@ describe('SecureSpacesClientWrapper', () => { ); expectAuthorizationCheck(securityExtension!, aliases); - expectAuditEvents(securityExtension!, aliases, { error: true }); expect(baseClient.disableLegacyUrlAliases).not.toHaveBeenCalled(); }); it('updates the legacy URL aliases when authorized', async () => { const { wrapper, baseClient, securityExtension } = setup({ securityEnabled }); - securityExtension!.performAuthorization.mockResolvedValue({ - // These values don't actually matter, the call to enforceAuthorization matters - status: 'fully_authorized', - typeMap: new Map(), - }); - // enforceAuthorization does *not* throw an error by default const aliases = [alias1, alias2]; await wrapper.disableLegacyUrlAliases(aliases); expectAuthorizationCheck(securityExtension!, aliases); - expectAuditEvents(securityExtension!, aliases, { error: false }); expect(baseClient.disableLegacyUrlAliases).toHaveBeenCalledTimes(1); expect(baseClient.disableLegacyUrlAliases).toHaveBeenCalledWith(aliases); }); diff --git a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts index ae89dc5991ee7..5449407f8d5a7 100644 --- a/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts +++ b/x-pack/plugins/security/server/spaces/secure_spaces_client_wrapper.ts @@ -7,19 +7,17 @@ import Boom from '@hapi/boom'; +import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; import type { ISavedObjectsSecurityExtension } from '@kbn/core-saved-objects-server'; -import { AuditAction } from '@kbn/core-saved-objects-server'; import type { KibanaRequest, SavedObjectsClient } from '@kbn/core/server'; import type { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult, ISpacesClient, - LegacyUrlAliasTarget, Space, } from '@kbn/spaces-plugin/server'; -import { ALL_SPACES_ID } from '../../common/constants'; import type { AuditLogger } from '../audit'; import { SpaceAuditAction, spaceAuditEvent } from '../audit'; import type { AuthorizationServiceSetup } from '../authorization'; @@ -278,20 +276,7 @@ export class SecureSpacesClientWrapper implements ISpacesClient { const finder = this.spacesClient.createSavedObjectFinder(id); try { for await (const response of finder.find()) { - response.saved_objects.forEach((savedObject) => { - const { namespaces = [] } = savedObject; - const isOnlySpace = namespaces.length === 1; // We can always rely on the `namespaces` field having >=1 element - if (namespaces.includes(ALL_SPACES_ID) && !namespaces.includes(id)) { - // This object exists in All Spaces and its `namespaces` field isn't going to change; there's nothing to audit - return; - } - securityExtension.addAuditEvent({ - action: isOnlySpace ? AuditAction.DELETE : AuditAction.UPDATE_OBJECTS_SPACES, - outcome: 'unknown', - savedObject: { type: savedObject.type, id: savedObject.id }, - ...(!isOnlySpace && { deleteFromSpaces: [id] }), - }); - }); + this.securityExtension?.auditObjectsForSpaceDeletion(id, response.saved_objects); } } finally { await finder.close(); @@ -310,46 +295,13 @@ export class SecureSpacesClientWrapper implements ISpacesClient { } public async disableLegacyUrlAliases(aliases: LegacyUrlAliasTarget[]) { - if (this.securityExtension) { - const [uniqueSpaces, typesAndSpaces] = aliases.reduce( - ([spaces, typesAndSpacesMap], { targetSpace, targetType }) => { - const spacesForType = typesAndSpacesMap.get(targetType) ?? new Set(); - return [ - spaces.add(targetSpace), - typesAndSpacesMap.set(targetType, spacesForType.add(targetSpace)), - ]; - }, - [new Set(), new Map>()] + try { + await this.securityExtension?.authorizeDisableLegacyUrlAliases(aliases); // will throw if unauthorized + } catch (err) { + throw this.errors.decorateForbiddenError( + new Error(`Unable to disable aliases: ${err.message}`) ); - - let error: Error | undefined; - try { - await this.securityExtension.performAuthorization({ - actions: new Set(['bulk_update']), - types: new Set(typesAndSpaces.keys()), - spaces: uniqueSpaces, - enforceMap: typesAndSpaces, - }); - } catch (err) { - error = this.errors.decorateForbiddenError( - new Error(`Unable to disable aliases: ${err.message}`) - ); - } - - for (const alias of aliases) { - const id = getAliasId(alias); - this.securityExtension.addAuditEvent({ - action: AuditAction.UPDATE, - savedObject: { type: LEGACY_URL_ALIAS_TYPE, id }, - error, - ...(!error && { outcome: 'unknown' }), // If authorization was a success, the outcome is unknown because the update operation has not occurred yet - }); - } - if (error) { - throw error; - } } - return this.spacesClient.disableLegacyUrlAliases(aliases); } diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index 1ca14763ef532..6106c3e34792b 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -49,9 +49,13 @@ "@kbn/logging-mocks", "@kbn/web-worker-stub", "@kbn/core-saved-objects-utils-server", + "@kbn/core-saved-objects-api-server", + "@kbn/core-saved-objects-base-server-internal", + "@kbn/core-saved-objects-common", "@kbn/core-custom-branding-browser-mocks", "@kbn/core-custom-branding-common", "@kbn/core-custom-branding-server-mocks", + "@kbn/ecs", "@kbn/shared-ux-router", ], "exclude": [ diff --git a/x-pack/plugins/security_solution/server/__mocks__/core.mock.ts b/x-pack/plugins/security_solution/server/__mocks__/core.mock.ts index 1ae9b046dc636..a0521b31ae904 100644 --- a/x-pack/plugins/security_solution/server/__mocks__/core.mock.ts +++ b/x-pack/plugins/security_solution/server/__mocks__/core.mock.ts @@ -8,7 +8,8 @@ // See: https://github.com/elastic/kibana/issues/117255, this creates mocks to avoid memory leaks from kibana core. // We _must_ import from the restricted path or we pull in _everything_ including memory leaks from Kibana core -import { SavedObjectsUtils, SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; module.exports = { SavedObjectsUtils, diff --git a/x-pack/plugins/spaces/common/index.ts b/x-pack/plugins/spaces/common/index.ts index 13a5d5e5ef5ae..18bc6bbaed8be 100644 --- a/x-pack/plugins/spaces/common/index.ts +++ b/x-pack/plugins/spaces/common/index.ts @@ -13,10 +13,4 @@ export { DEFAULT_SPACE_ID, } from './constants'; export { addSpaceIdToPath, getSpaceIdFromPath } from './lib/spaces_url_parser'; -export type { - Space, - GetAllSpacesOptions, - GetAllSpacesPurpose, - GetSpaceResult, - LegacyUrlAliasTarget, -} from './types'; +export type { Space, GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult } from './types'; diff --git a/x-pack/plugins/spaces/common/types.ts b/x-pack/plugins/spaces/common/types.ts index 39864447310b4..0d0a32d758da8 100644 --- a/x-pack/plugins/spaces/common/types.ts +++ b/x-pack/plugins/spaces/common/types.ts @@ -103,21 +103,3 @@ export interface GetSpaceResult extends Space { */ authorizedPurposes?: Record; } - -/** - * Client interface for interacting with legacy URL aliases. - */ -export interface LegacyUrlAliasTarget { - /** - * The namespace that the object existed in when it was converted. - */ - targetSpace: string; - /** - * The type of the object when it was converted. - */ - targetType: string; - /** - * The original ID of the object, before it was converted. - */ - sourceId: string; -} diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/types.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/types.ts index ba39dd2499f4c..9e16925451e4f 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/types.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { LegacyUrlAliasTarget } from '../../../common'; +import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; export interface InternalLegacyUrlAliasTarget extends LegacyUrlAliasTarget { /** diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts index 50b9660dc0577..e08e8c4b29ae0 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts @@ -10,14 +10,10 @@ import { BehaviorSubject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; import type { SavedObjectsCollectMultiNamespaceReferencesResponse } from '@kbn/core-saved-objects-api-server'; +import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; import type { HttpSetup } from '@kbn/core/public'; -import type { - GetAllSpacesOptions, - GetSpaceResult, - LegacyUrlAliasTarget, - Space, -} from '../../common'; +import type { GetAllSpacesOptions, GetSpaceResult, Space } from '../../common'; import type { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; interface SavedObjectTarget { diff --git a/x-pack/plugins/spaces/server/index.ts b/x-pack/plugins/spaces/server/index.ts index bad6969f2a3a1..b866042f991ee 100644 --- a/x-pack/plugins/spaces/server/index.ts +++ b/x-pack/plugins/spaces/server/index.ts @@ -27,13 +27,7 @@ export type { SpacesClientWrapper, } from './spaces_client'; -export type { - Space, - GetAllSpacesOptions, - GetAllSpacesPurpose, - GetSpaceResult, - LegacyUrlAliasTarget, -} from '../common'; +export type { Space, GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult } from '../common'; export const config: PluginConfigDescriptor = { schema: ConfigSchema, diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts index cfc001d570c8d..b06187c032662 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts @@ -8,19 +8,14 @@ import Boom from '@hapi/boom'; import { omit } from 'lodash'; +import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common'; import type { ISavedObjectsPointInTimeFinder, ISavedObjectsRepository, SavedObject, } from '@kbn/core/server'; -import type { - GetAllSpacesOptions, - GetAllSpacesPurpose, - GetSpaceResult, - LegacyUrlAliasTarget, - Space, -} from '../../common'; +import type { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult, Space } from '../../common'; import { isReservedSpace } from '../../common'; import type { ConfigType } from '../config'; diff --git a/x-pack/plugins/spaces/tsconfig.json b/x-pack/plugins/spaces/tsconfig.json index 735c6eedbdb16..3db72a1217db3 100644 --- a/x-pack/plugins/spaces/tsconfig.json +++ b/x-pack/plugins/spaces/tsconfig.json @@ -27,6 +27,7 @@ "@kbn/test", "@kbn/utils", "@kbn/shared-ux-page-kibana-template", + "@kbn/core-saved-objects-common", "@kbn/shared-ux-router", "@kbn/core-custom-branding-browser-mocks", "@kbn/core-custom-branding-common", From 241b5b779989e9a32d07790850d4fff2d7e301e6 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Wed, 15 Feb 2023 09:41:12 -0600 Subject: [PATCH 27/68] Enterprise Search: include search experiences engines side nav (#151227) ## Summary When initially implementing the updates side nav with the feature flagged Engines feature, we did not include Search Experiences in the nav. Upon learning more about this page I think its best we keep it included in the nav as long as it's still included in the shortcut nav and has a guided onboarding step. We may still remove this before engines is released in 8.8, but we can remove it with those other usages as well then. ### Screenshots ![image](https://user-images.githubusercontent.com/1972968/218875418-f7543f8d-cabf-4f7e-b2ad-c7f393e1d348.png) --- .../public/applications/shared/layout/nav.test.tsx | 10 ++++++++++ .../public/applications/shared/layout/nav.tsx | 10 ++++++++++ 2 files changed, 20 insertions(+) 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 7bb55a4ac8b2d..4433d7d4a5804 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 @@ -258,6 +258,11 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { name: 'Engines', href: '/app/enterprise_search/content/engines', }, + { + id: 'searchExperiences', + name: 'Search Experiences', + href: '/app/enterprise_search/search_experiences', + }, ], }, { @@ -399,6 +404,11 @@ describe('useEnterpriseSearchEngineNav', () => { name: 'Engines', href: '/app/enterprise_search/content/engines', }, + { + id: 'searchExperiences', + name: 'Search Experiences', + href: '/app/enterprise_search/search_experiences', + }, ], }, { 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 db5cc82d166b4..bd28fb86175f5 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 @@ -179,6 +179,16 @@ export const useEnterpriseSearchNav = () => { to: ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + ENGINES_PATH, }), }, + { + id: 'searchExperiences', + name: i18n.translate('xpack.enterpriseSearch.nav.searchExperiencesTitle', { + defaultMessage: 'Search Experiences', + }), + ...generateNavLink({ + shouldNotCreateHref: true, + to: SEARCH_EXPERIENCES_PLUGIN.URL, + }), + }, ], name: i18n.translate('xpack.enterpriseSearch.nav.searchTitle', { defaultMessage: 'Search', From 7906c76749fa89f56df626e1e4012e01bfe14753 Mon Sep 17 00:00:00 2001 From: "Quynh Nguyen (Quinn)" <43350163+qn895@users.noreply.github.com> Date: Wed, 15 Feb 2023 09:47:47 -0600 Subject: [PATCH 28/68] [ML] Add functional tests for Field stats flyout in Transforms & DFA creation wizard (#150927) --- .../field_stats_content.tsx | 2 +- .../field_stats_flyout/field_stats_flyout.tsx | 8 +- .../field_stats_info_button.tsx | 2 +- .../classification_creation.ts | 33 +++- .../classification_creation_saved_search.ts | 63 +++++++- .../outlier_detection_creation.ts | 25 ++- .../regression_creation.ts | 46 +++++- .../apps/ml/data_frame_analytics/types.ts | 8 + .../index_pattern/creation_index_pattern.ts | 87 ++++++++++- .../test/functional/apps/transform/helpers.ts | 8 + .../ml/data_frame_analytics_creation.ts | 44 +++++- .../services/ml/field_stats_flyout.ts | 144 ++++++++++++++++++ x-pack/test/functional/services/ml/index.ts | 6 +- .../functional/services/transform/wizard.ts | 44 ++++++ 14 files changed, 496 insertions(+), 24 deletions(-) create mode 100644 x-pack/test/functional/apps/ml/data_frame_analytics/types.ts create mode 100644 x-pack/test/functional/services/ml/field_stats_flyout.ts diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_content.tsx b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_content.tsx index ddf082cbd7e96..c73bdf49b031a 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_content.tsx +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_content.tsx @@ -62,7 +62,7 @@ export const FieldStatsContent: FC<{ toDate={timeRange.to} dataViewOrDataViewId={currentDataView} field={fieldForStats} - data-test-subj={`jobCreatorFieldStatsPopover ${fieldForStats.name}`} + data-test-subj={`mlFieldStatsFlyoutContent ${fieldForStats.name}`} color={DEFAULT_COLOR} /> ) : null; diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout.tsx b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout.tsx index 89d53a1fe0ba3..6c8c7612d8bbf 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_flyout.tsx @@ -40,7 +40,13 @@ export const FieldStatsFlyout: FC<{ if (isFlyoutVisible) { return ( - +

diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx index dd500416c8fb6..728ae5ed0f3af 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx @@ -36,6 +36,7 @@ export const FieldStatsInfoButton = ({ )} > diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts index 2ba4ac6f08350..df6d53974d876 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import type { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import type { FieldStatsType } from './types'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); @@ -16,7 +17,7 @@ export default function ({ getService }: FtrProviderContext) { describe('classification creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification'); - await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing', '@timestamp'); + await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -49,6 +50,18 @@ export default function ({ getService }: FtrProviderContext) { trainingPercent: 20, modelMemory: '60mb', createIndexPattern: true, + fieldStatsEntries: [ + { + fieldName: 'age', + type: 'number' as FieldStatsType, + isDependentVariableInput: true, + }, + { + fieldName: 'balance.keyword', + type: 'keyword' as FieldStatsType, + isDependentVariableInput: true, + }, + ], expected: { rocCurveColorState: [ // tick/grid/axis @@ -138,8 +151,20 @@ export default function ({ getService }: FtrProviderContext) { testData.expected.runtimeFieldsEditorContent ); - await ml.testExecution.logTestStep('inputs the dependent variable'); + await ml.testExecution.logTestStep( + 'opens field stats flyout from dependent variable input' + ); await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists(); + for (const { fieldName, type: fieldType } of testData.fieldStatsEntries.filter( + (e) => e.isDependentVariableInput + )) { + await ml.dataFrameAnalyticsCreation.assertFieldStatsFlyoutContentFromDependentVariableInputTrigger( + fieldName, + fieldType + ); + } + + await ml.testExecution.logTestStep('inputs the dependent variable'); await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable); await ml.testExecution.logTestStep('inputs the training percent'); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts index b47a80c3041e2..872ffb2294af9 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation_saved_search.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; -import { FtrProviderContext } from '../../../ftr_provider_context'; +import type { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import type { FieldStatsType } from './types'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); @@ -38,6 +39,20 @@ export default function ({ getService }: FtrProviderContext) { }); const dateNow = Date.now(); + const fieldStatsEntries = [ + { + fieldName: 'airline', + type: 'keyword' as FieldStatsType, + isDependentVariableInput: true, + isIncludeFieldInput: true, + }, + { + fieldName: '@version', + type: 'keyword' as FieldStatsType, + isIncludeFieldInput: true, + }, + ]; + const testDataList = [ { suiteTitle: 'with lucene query', @@ -48,6 +63,7 @@ export default function ({ getService }: FtrProviderContext) { get destinationIndex(): string { return `user-${this.jobId}`; }, + fieldStatsEntries, runtimeFields: { uppercase_airline: { type: 'keyword', @@ -59,6 +75,10 @@ export default function ({ getService }: FtrProviderContext) { modelMemory: '20mb', createIndexPattern: true, expected: { + fieldStatsValues: { airline: ['AAL', 'AWE', 'ASA', 'ACA', 'AMX'] } as Record< + string, + string[] + >, source: 'ft_farequote_small', rocCurveColorState: [ // tick/grid/axis @@ -99,6 +119,7 @@ export default function ({ getService }: FtrProviderContext) { get destinationIndex(): string { return `user-${this.jobId}`; }, + fieldStatsEntries, runtimeFields: { uppercase_airline: { type: 'keyword', @@ -110,6 +131,10 @@ export default function ({ getService }: FtrProviderContext) { modelMemory: '20mb', createIndexPattern: true, expected: { + fieldStatsValues: { airline: ['AAL', 'AWE', 'ASA', 'ACA', 'AMX'] } as Record< + string, + string[] + >, source: 'ft_farequote_small', rocCurveColorState: [ // tick/grid/axis @@ -150,6 +175,7 @@ export default function ({ getService }: FtrProviderContext) { get destinationIndex(): string { return `user-${this.jobId}`; }, + fieldStatsEntries, runtimeFields: { uppercase_airline: { type: 'keyword', @@ -161,6 +187,9 @@ export default function ({ getService }: FtrProviderContext) { modelMemory: '20mb', createIndexPattern: true, expected: { + fieldStatsValues: { + airline: ['AAL', 'ASA'], + } as Record, source: 'ft_farequote_small', rocCurveColorState: [ // tick/grid/axis @@ -213,6 +242,7 @@ export default function ({ getService }: FtrProviderContext) { modelMemory: '20mb', createIndexPattern: true, expected: { + fieldStatsValues: { airline: ['ASA', 'FFT'] } as Record, source: 'ft_farequote_small', rocCurveColorState: [ // tick/grid/axis @@ -293,9 +323,23 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataFrameAnalyticsCreation.assertRuntimeMappingsEditorContent( testData.expected.runtimeFieldsEditorContent ); + await ml.testExecution.logTestStep( + 'opens field stats flyout from dependent variable input' + ); + await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists(); + for (const { fieldName, type: fieldType } of fieldStatsEntries.filter( + (e) => e.isDependentVariableInput + )) { + await ml.dataFrameAnalyticsCreation.assertFieldStatsFlyoutContentFromDependentVariableInputTrigger( + fieldName, + fieldType, + testData.expected.fieldStatsValues && fieldName in testData.expected.fieldStatsValues + ? (testData.expected.fieldStatsValues[fieldName] as string[]) + : undefined + ); + } await ml.testExecution.logTestStep('inputs the dependent variable'); - await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists(); await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable); await ml.testExecution.logTestStep('inputs the training percent'); @@ -308,6 +352,19 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('displays the include fields selection'); await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); + await ml.testExecution.logTestStep('opens field stats flyout from include fields input'); + for (const { fieldName, type: fieldType } of fieldStatsEntries.filter( + (e) => e.isIncludeFieldInput + )) { + await ml.dataFrameAnalyticsCreation.assertFieldStatFlyoutContentFromIncludeFieldTrigger( + fieldName, + fieldType, + testData.expected.fieldStatsValues && fieldName in testData.expected.fieldStatsValues + ? (testData.expected.fieldStatsValues[fieldName] as string[]) + : undefined + ); + } + await ml.testExecution.logTestStep('continues to the additional options step'); await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep(); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts index 21dd61e9d5249..576fcc259315d 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; -import { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import type { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import type { FieldStatsType } from './types'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); @@ -16,7 +17,7 @@ export default function ({ getService }: FtrProviderContext) { describe('outlier detection creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ihp_outlier'); - await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier', '@timestamp'); + await ml.testResources.createIndexPatternIfNeeded('ft_ihp_outlier'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -29,6 +30,14 @@ export default function ({ getService }: FtrProviderContext) { const jobId = `ihp_1_${Date.now()}`; + const fieldStatsEntries = [ + { + fieldName: '1stFlrSF', + type: 'keyword' as FieldStatsType, + isIncludeFieldInput: true, + }, + ]; + const testDataList = [ { suiteTitle: 'iowa house prices', @@ -171,6 +180,16 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('displays the include fields selection'); await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); + await ml.testExecution.logTestStep('opens field stats flyout from include fields input'); + for (const { fieldName, type: fieldType } of fieldStatsEntries.filter( + (e) => e.isIncludeFieldInput + )) { + await ml.dataFrameAnalyticsCreation.assertFieldStatFlyoutContentFromIncludeFieldTrigger( + fieldName, + fieldType + ); + } + await ml.testExecution.logTestStep( 'sets the sample size to 10000 for the scatterplot matrix' ); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts index 7a84c41aa4a66..4f89d5875165d 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts @@ -5,18 +5,33 @@ * 2.0. */ -import { FtrProviderContext } from '../../../ftr_provider_context'; -import { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; +import type { AnalyticsTableRowDetails } from '../../../services/ml/data_frame_analytics_table'; +import type { FieldStatsType } from './types'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); const editedDescription = 'Edited description'; + const fieldStatsEntries = [ + { + fieldName: 'g1', + type: 'number' as FieldStatsType, + isDependentVariableInput: true, + isIncludeFieldInput: true, + }, + { + fieldName: 'g2', + type: 'number' as FieldStatsType, + isIncludeFieldInput: true, + }, + ]; + describe('regression creation', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/egs_regression'); - await ml.testResources.createIndexPatternIfNeeded('ft_egs_regression', '@timestamp'); + await ml.testResources.createIndexPatternIfNeeded('ft_egs_regression'); await ml.testResources.setKibanaTimeZoneToUTC(); await ml.securityUI.loginAsMlPowerUser(); @@ -38,6 +53,7 @@ export default function ({ getService }: FtrProviderContext) { get destinationIndex(): string { return `user-${jobId}`; }, + fieldStatsEntries, runtimeFields: { uppercase_stab: { type: 'keyword', @@ -132,8 +148,20 @@ export default function ({ getService }: FtrProviderContext) { testData.expected.runtimeFieldsEditorContent ); - await ml.testExecution.logTestStep('inputs the dependent variable'); + await ml.testExecution.logTestStep( + 'opens field stats flyout from dependent variable input' + ); await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists(); + for (const { fieldName, type: fieldType } of fieldStatsEntries.filter( + (e) => e.isDependentVariableInput + )) { + await ml.dataFrameAnalyticsCreation.assertFieldStatsFlyoutContentFromDependentVariableInputTrigger( + fieldName, + fieldType + ); + } + + await ml.testExecution.logTestStep('inputs the dependent variable'); await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable); await ml.testExecution.logTestStep('inputs the training percent'); @@ -146,6 +174,16 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('displays the include fields selection'); await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists(); + await ml.testExecution.logTestStep('opens field stats flyout from include fields input'); + for (const { fieldName, type: fieldType } of fieldStatsEntries.filter( + (e) => e.isIncludeFieldInput + )) { + await ml.dataFrameAnalyticsCreation.assertFieldStatFlyoutContentFromIncludeFieldTrigger( + fieldName, + fieldType + ); + } + await ml.testExecution.logTestStep( 'sets the sample size to 10000 for the scatterplot matrix' ); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/types.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/types.ts new file mode 100644 index 0000000000000..c80873334c7a1 --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/types.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 type FieldStatsType = 'number' | 'keyword' | 'date'; diff --git a/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts index a383d471ceccd..e8ded6f662867 100644 --- a/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts +++ b/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts @@ -7,7 +7,7 @@ import { TRANSFORM_STATE } from '@kbn/transform-plugin/common/constants'; -import { FtrProviderContext } from '../../../../ftr_provider_context'; +import type { FtrProviderContext } from '../../../../ftr_provider_context'; import { GroupByEntry, isLatestTransformTestData, @@ -36,12 +36,38 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce'); }); + const fieldStatsEntries = [ + { + identifier: 'terms(customer_gender)', + fieldName: 'customer_gender', + type: 'keyword', + isGroupByInput: true, + }, + { + fieldName: 'category.keyword', + type: 'keyword', + isAggInput: true, + isUniqueKeyInput: true, + }, + { + fieldName: 'currency', + type: 'keyword', + isAggInput: true, + isUniqueKeyInput: true, + }, + { + fieldName: 'order_date', + type: 'date', + isSortFieldInput: true, + }, + ]; const DEFAULT_NUM_FAILURE_RETRIES = '5'; const testDataList: Array = [ { type: 'pivot', suiteTitle: 'batch transform with terms+date_histogram groups and avg agg', source: 'ft_ecommerce', + fieldStatsEntries, groupByEntries: [ { identifier: 'terms(category)', @@ -419,6 +445,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { type: 'latest', suiteTitle: 'batch transform with the latest function', source: 'ft_ecommerce', + fieldStatsEntries, uniqueKeys: [ { identifier: 'geoip.country_iso_code', @@ -570,7 +597,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await canvasElement.resetAntiAliasing(); if (isPivotTransformTestData(testData)) { - await transform.testExecution.logTestStep('adds the group by entries'); + await transform.testExecution.logTestStep( + 'opens field stats flyout from group by input' + ); + + const groupByFieldStatsEntries = testData.fieldStatsEntries + ? testData.fieldStatsEntries.filter((e) => e.isGroupByInput) + : []; + + for (const { fieldName, type } of groupByFieldStatsEntries) { + await transform.wizard.assertFieldStatFlyoutContentFromGroupByInputTrigger( + fieldName, + type + ); + } + for (const [index, entry] of testData.groupByEntries.entries()) { await transform.wizard.assertGroupByInputExists(); await transform.wizard.assertGroupByInputValue([]); @@ -582,6 +623,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); } + await transform.testExecution.logTestStep('opens field stats flyout from agg input'); + const aggInputFieldStatsEntries = testData.fieldStatsEntries + ? testData.fieldStatsEntries.filter((e) => e.isAggInput) + : []; + + for (const { fieldName, type } of aggInputFieldStatsEntries) { + await transform.wizard.assertFieldStatFlyoutContentFromAggInputTrigger( + fieldName, + type + ); + } + await transform.testExecution.logTestStep('adds the aggregation entries'); await transform.wizard.addAggregationEntries(testData.aggregationEntries); @@ -599,12 +652,42 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { if (isLatestTransformTestData(testData)) { await transform.testExecution.logTestStep('sets latest transform method'); await transform.wizard.selectTransformFunction('latest'); + + await transform.testExecution.logTestStep( + 'opens field stats flyout from unique keys input' + ); + const uniqueKeyInputFieldStatsEntries = testData.fieldStatsEntries + ? testData.fieldStatsEntries.filter((e) => e.isUniqueKeyInput) + : []; + + for (const { fieldName, type } of uniqueKeyInputFieldStatsEntries) { + await transform.wizard.assertFieldStatsFlyoutContentFromUniqueKeysInputTrigger( + fieldName, + type + ); + } + await transform.testExecution.logTestStep('adds unique keys'); for (const { identifier, label } of testData.uniqueKeys) { await transform.wizard.assertUniqueKeysInputExists(); await transform.wizard.assertUniqueKeysInputValue([]); await transform.wizard.addUniqueKeyEntry(identifier, label); } + + await transform.testExecution.logTestStep( + 'opens field stats flyout from sort by input' + ); + const sortFieldInputFieldStatsEntries = testData.fieldStatsEntries + ? testData.fieldStatsEntries.filter((e) => e.isSortFieldInput) + : []; + + for (const { fieldName, type } of sortFieldInputFieldStatsEntries) { + await transform.wizard.assertFieldStatFlyoutContentFromSortFieldInputTrigger( + fieldName, + type + ); + } + await transform.testExecution.logTestStep('sets the sort field'); await transform.wizard.assertSortFieldInputExists(); await transform.wizard.assertSortFieldInputValue(''); diff --git a/x-pack/test/functional/apps/transform/helpers.ts b/x-pack/test/functional/apps/transform/helpers.ts index c422ea6dfca0f..d4b163871cc4c 100644 --- a/x-pack/test/functional/apps/transform/helpers.ts +++ b/x-pack/test/functional/apps/transform/helpers.ts @@ -30,6 +30,14 @@ export interface BaseTransformTestData { destinationDataViewTimeField?: string; discoverAdjustSuperDatePicker: boolean; numFailureRetries?: string; + fieldStatsEntries?: Array<{ + fieldName: string; + type: 'keyword' | 'number' | 'date'; + isGroupByInput?: boolean; + isAggInput?: boolean; + isUniqueKeyInput?: boolean; + isSortFieldInput?: boolean; + }>; } export interface PivotTransformTestData extends BaseTransformTestData { diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 91ff6af4f5397..c20e73d0f51f8 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -15,20 +15,22 @@ import { import { FtrProviderContext } from '../../ftr_provider_context'; import type { CanvasElementColorStats } from '../canvas_element'; import type { MlCommonUI } from './common_ui'; -import { MlApi } from './api'; +import type { MlApi } from './api'; +import type { MlFieldStatsFlyout } from './field_stats_flyout'; export function MachineLearningDataFrameAnalyticsCreationProvider( { getPageObject, getService }: FtrProviderContext, mlCommonUI: MlCommonUI, - mlApi: MlApi + mlApi: MlApi, + mlFieldStatsFlyout: MlFieldStatsFlyout ) { const headerPage = getPageObject('header'); const commonPage = getPageObject('common'); - const testSubjects = getService('testSubjects'); + const aceEditor = getService('aceEditor'); const comboBox = getService('comboBox'); const retry = getService('retry'); - const aceEditor = getService('aceEditor'); + const testSubjects = getService('testSubjects'); return { async assertJobTypeSelectExists() { @@ -202,6 +204,19 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }); }, + async assertFieldStatFlyoutContentFromIncludeFieldTrigger( + fieldName: string, + fieldType: 'keyword' | 'date' | 'number', + expectedContent?: string[] + ) { + await mlFieldStatsFlyout.assertFieldStatFlyoutContentFromTrigger( + 'mlAnalyticsCreateJobWizardIncludesSelect', + fieldName, + fieldType, + expectedContent + ); + }, + async assertIncludeFieldsSelectionExists() { await testSubjects.existOrFail('mlAnalyticsCreateJobWizardIncludesTable', { timeout: 8000 }); @@ -255,6 +270,27 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }); }, + async assertFieldStatsFlyoutContentFromDependentVariableInputTrigger( + fieldName: string, + fieldType: 'keyword' | 'date' | 'number', + expectedContent?: string[] + ) { + await mlFieldStatsFlyout.assertFieldStatFlyoutContentFromComboBoxTrigger( + 'mlAnalyticsCreateJobWizardDependentVariableSelect loaded', + fieldName, + fieldType, + expectedContent + ); + }, + + async assertFieldStatTopValuesContent( + fieldName: string, + fieldType: 'keyword' | 'date' | 'number', + expectedContent: string[] + ) { + await mlFieldStatsFlyout.assertTopValuesContent(fieldName, fieldType, expectedContent); + }, + async assertDependentVariableInputMissing() { await testSubjects.missingOrFail( '~mlAnalyticsCreateJobWizardDependentVariableSelect > comboBoxInput' diff --git a/x-pack/test/functional/services/ml/field_stats_flyout.ts b/x-pack/test/functional/services/ml/field_stats_flyout.ts new file mode 100644 index 0000000000000..826af0a4e31c7 --- /dev/null +++ b/x-pack/test/functional/services/ml/field_stats_flyout.ts @@ -0,0 +1,144 @@ +/* + * 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 { ProvidedType } from '@kbn/test'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export type MlFieldStatsFlyout = ProvidedType; + +export function MachineLearningFieldStatsFlyoutProvider({ getService }: FtrProviderContext) { + const browser = getService('browser'); + const comboBox = getService('comboBox'); + const find = getService('find'); + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + + return { + async assertFieldStatContentByType( + testSubj: string, + fieldName: string, + fieldType: 'keyword' | 'date' | 'number' + ) { + await retry.tryForTime(2000, async () => { + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); + + if (fieldType === 'date') { + await testSubjects.existOrFail(`mlFieldStatsFlyoutContent ${fieldName}-histogram`); + } + + if (fieldType === 'keyword') { + await testSubjects.existOrFail(`mlFieldStatsFlyoutContent ${fieldName}-topValues`); + } + + // For numeric fields, we expect both the top values and the distribution chart + if (fieldType === 'number') { + // Assert top values exist + await testSubjects.existOrFail( + `mlFieldStatsFlyoutContent ${fieldName}-buttonGroup-topValuesButton` + ); + await testSubjects.click( + `mlFieldStatsFlyoutContent ${fieldName}-buttonGroup-topValuesButton` + ); + + await testSubjects.existOrFail(`mlFieldStatsFlyoutContent ${fieldName}-topValues`); + + // Assert distribution chart exists + await testSubjects.existOrFail( + `mlFieldStatsFlyoutContent ${fieldName}-buttonGroup-distributionButton` + ); + await testSubjects.click( + `mlFieldStatsFlyoutContent ${fieldName}-buttonGroup-distributionButton` + ); + expect( + await find.existsByCssSelector('[data-test-subj="mlFieldStatsFlyout"] .echChart') + ).to.eql(true); + } + }); + }, + + async assertFieldStatFlyoutContentFromTrigger( + testSubj: string, + fieldName: string, + fieldType: 'keyword' | 'date' | 'number', + expectedTopValuesContent?: string[] + ) { + const selector = `~${testSubj} > ~mlInspectFieldStatsButton-${fieldName}`; + + await retry.tryForTime(2000, async () => { + await testSubjects.existOrFail(selector); + await testSubjects.click(selector); + await testSubjects.existOrFail('mlFieldStatsFlyout'); + await testSubjects.existOrFail(`mlFieldStatsFlyoutContent ${fieldName}-title`); + }); + await this.assertFieldStatContentByType(testSubj, fieldName, fieldType); + if (Array.isArray(expectedTopValuesContent)) { + await this.assertTopValuesContent(fieldName, fieldType, expectedTopValuesContent); + } + await this.ensureFieldStatsFlyoutClosed(); + }, + + async assertFieldStatFlyoutContentFromComboBoxTrigger( + parentComboBoxSelector: string, + fieldName: string, + fieldType: 'keyword' | 'date' | 'number', + expectedTopValuesContent?: string[] + ) { + const selector = `mlInspectFieldStatsButton-${fieldName}`; + + await retry.tryForTime(2000, async () => { + const fieldTarget = await testSubjects.find(parentComboBoxSelector); + await comboBox.openOptionsList(fieldTarget); + + await testSubjects.existOrFail(selector); + await testSubjects.click(selector); + await testSubjects.existOrFail('mlFieldStatsFlyout'); + + await testSubjects.existOrFail(`mlFieldStatsFlyoutContent ${fieldName}-title`); + + await this.assertFieldStatContentByType(parentComboBoxSelector, fieldName, fieldType); + + if (Array.isArray(expectedTopValuesContent)) { + await this.assertTopValuesContent(fieldName, fieldType, expectedTopValuesContent); + } + await this.ensureFieldStatsFlyoutClosed(); + }); + }, + + async assertTopValuesContent(fieldName: string, fieldType: string, expectedValues: string[]) { + await retry.tryForTime(2000, async () => { + // check for top values rows + await testSubjects.existOrFail(`mlFieldStatsFlyoutContent ${fieldName}-topValues`); + const topValuesRows = await testSubjects.findAll( + `mlFieldStatsFlyoutContent ${fieldName}-topValues-formattedFieldValue` + ); + expect(topValuesRows.length).to.eql( + expectedValues.length, + `Expected top values to have ${expectedValues.length} (got ${topValuesRows.length})` + ); + for (const [idx, expectedValue] of expectedValues.entries()) { + const actualValue = await topValuesRows[idx].getVisibleText(); + expect(actualValue).to.eql( + expectedValue, + `Expected top value row to be ${expectedValue} (got ${actualValue})` + ); + } + }); + }, + + async ensureFieldStatsFlyoutClosed() { + const flyoutIsOpen = await testSubjects.exists('mlFieldStatsFlyout'); + if (flyoutIsOpen) { + await retry.tryForTime(2000, async () => { + await testSubjects.click('mlFieldStatsFlyout > euiFlyoutCloseButton'); + await testSubjects.missingOrFail('mlFieldStatsFlyout'); + }); + } + }, + }; +} diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index 4ac41a25a556e..ca9698b7813e5 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -62,11 +62,13 @@ import { MachineLearningCasesProvider } from './cases'; import { AnomalyChartsProvider } from './anomaly_charts'; import { NotificationsProvider } from './notifications'; import { MlTableServiceProvider } from './common_table_service'; +import { MachineLearningFieldStatsFlyoutProvider } from './field_stats_flyout'; export function MachineLearningProvider(context: FtrProviderContext) { const commonAPI = MachineLearningCommonAPIProvider(context); const commonUI = MachineLearningCommonUIProvider(context); const commonDataGrid = MachineLearningCommonDataGridProvider(context); + const commonFieldStatsFlyout = MachineLearningFieldStatsFlyoutProvider(context); const anomaliesTable = MachineLearningAnomaliesTableProvider(context); const anomalyCharts = AnomalyChartsProvider(context); @@ -85,7 +87,8 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dataFrameAnalyticsCreation = MachineLearningDataFrameAnalyticsCreationProvider( context, commonUI, - api + api, + commonFieldStatsFlyout ); const dataFrameAnalyticsEdit = MachineLearningDataFrameAnalyticsEditProvider(context, commonUI); const dataFrameAnalyticsResults = MachineLearningDataFrameAnalyticsResultsProvider( @@ -149,6 +152,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { commonAPI, commonConfig, commonDataGrid, + commonFieldStatsFlyout, commonUI, customUrls, dashboardJobSelectionTable, diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index e1370706d2902..69332c8947d58 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -378,6 +378,17 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi await this.assertSelectedTransformFunction(transformFunction); }, + async assertFieldStatsFlyoutContentFromUniqueKeysInputTrigger( + fieldName: string, + fieldType: 'keyword' | 'date' | 'number' + ) { + await ml.commonFieldStatsFlyout.assertFieldStatFlyoutContentFromComboBoxTrigger( + 'transformWizardUniqueKeysSelector', + fieldName, + fieldType + ); + }, + async assertUniqueKeysInputExists() { await testSubjects.existOrFail('transformWizardUniqueKeysSelector > comboBoxInput'); }, @@ -405,6 +416,17 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi ]); }, + async assertFieldStatFlyoutContentFromSortFieldInputTrigger( + fieldName: string, + fieldType: 'keyword' | 'date' | 'number' + ) { + await ml.commonFieldStatsFlyout.assertFieldStatFlyoutContentFromComboBoxTrigger( + 'transformWizardSortFieldSelector', + fieldName, + fieldType + ); + }, + async assertSortFieldInputExists() { await testSubjects.existOrFail('transformWizardSortFieldSelector > comboBoxInput'); }, @@ -426,6 +448,17 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi await this.assertSortFieldInputValue(identificator); }, + async assertFieldStatFlyoutContentFromGroupByInputTrigger( + fieldName: string, + fieldType: 'keyword' | 'date' | 'number' + ) { + await ml.commonFieldStatsFlyout.assertFieldStatFlyoutContentFromComboBoxTrigger( + 'transformGroupBySelection', + fieldName, + fieldType + ); + }, + async assertGroupByInputExists() { await testSubjects.existOrFail('transformGroupBySelection > comboBoxInput'); }, @@ -485,6 +518,17 @@ export function TransformWizardProvider({ getService, getPageObjects }: FtrProvi } > comboBoxInput`; }, + async assertFieldStatFlyoutContentFromAggInputTrigger( + fieldName: string, + fieldType: 'keyword' | 'date' | 'number' + ) { + await ml.commonFieldStatsFlyout.assertFieldStatFlyoutContentFromComboBoxTrigger( + 'transformAggregationSelection', + fieldName, + fieldType + ); + }, + async assertAggregationInputExists(parentSelector?: string) { await testSubjects.existOrFail(this.getAggComboBoxInputSelector(parentSelector)); }, From a37dea72926f669c248aa28f22cfa1a4d7280dee Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 15 Feb 2023 16:02:17 +0000 Subject: [PATCH 29/68] [Fleet] Add tests to verify package policy bulk upgrade API creates correct assets (#151174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Closes #149423 Turns out no code changes needed after https://github.com/elastic/kibana/pull/150199 🥳 Add tests for the `/api/fleet/package_policies/upgrade` and `/api/fleet/package_policies/upgrade/dryrun` endpoints to verify they create the package policy assets when upgrading from an integration of type integration to type input. --- .../scripts/create_agents/create_agents.ts | 2 +- .../3.0.0/agent/input/input.yml.hbs | 18 +++ .../integration_to_input/3.0.0/changelog.yml | 16 ++ .../integration_to_input/3.0.0/docs/README.md | 1 + .../3.0.0/fields/input.yml | 4 + .../3.0.0/img/sample-logo.svg | 1 + .../3.0.0/img/sample-screenshot.png | Bin 0 -> 18849 bytes .../integration_to_input/3.0.0/manifest.yml | 45 ++++++ .../apis/package_policy/upgrade.ts | 139 +++++++++++++++++- 9 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/agent/input/input.yml.hbs create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/changelog.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/docs/README.md create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/fields/input.yml create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/img/sample-logo.svg create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/img/sample-screenshot.png create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/manifest.yml diff --git a/x-pack/plugins/fleet/scripts/create_agents/create_agents.ts b/x-pack/plugins/fleet/scripts/create_agents/create_agents.ts index 683d45a03757c..4436c07cc07b9 100644 --- a/x-pack/plugins/fleet/scripts/create_agents/create_agents.ts +++ b/x-pack/plugins/fleet/scripts/create_agents/create_agents.ts @@ -337,7 +337,7 @@ export async function run() { Array(currentBatchSize) .fill(0) .map(async (__, i) => { - const agentPolicyId = 'script-create-agent-' + uuidv4(); + const agentPolicyId = uuidv4(); const agentPolicy = await createAgentPolicy(agentPolicyId); logger.info(`Created agent policy ${agentPolicy.item.id}`); diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/agent/input/input.yml.hbs b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/agent/input/input.yml.hbs new file mode 100644 index 0000000000000..1ba86fa98a2f8 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/agent/input/input.yml.hbs @@ -0,0 +1,18 @@ +paths: +{{#each paths}} + - {{this}} +{{/each}} + +{{#if tags}} +tags: +{{#each tags as |tag i|}} + - {{tag}} +{{/each}} +{{/if}} + +{{#if pipeline}} +pipeline: {{pipeline}} +{{/if}} + +data_stream: + dataset: {{data_stream.dataset}} \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/changelog.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/changelog.yml new file mode 100644 index 0000000000000..e7244221457b0 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/changelog.yml @@ -0,0 +1,16 @@ +# newer versions go on top +- version: "3.0.0" + changes: + - description: Remove required variable + type: enhancement + link: http://some.url +- version: "2.0.0" + changes: + - description: Changed to input pkg + type: enhancement + link: http://some.url +- version: "1.0.0" + changes: + - description: test + type: enhancement + link: http://some.url diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/docs/README.md b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/docs/README.md new file mode 100644 index 0000000000000..9f29c89e0f5ef --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/docs/README.md @@ -0,0 +1 @@ +# Custom Logs \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/fields/input.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/fields/input.yml new file mode 100644 index 0000000000000..f5851c64b6b3a --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/fields/input.yml @@ -0,0 +1,4 @@ +- name: input.name + type: constant_keyword + description: Sample field to be added. + value: logs \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/img/sample-logo.svg b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/img/sample-logo.svg new file mode 100644 index 0000000000000..6268dd88f3b3d --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/img/sample-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/img/sample-screenshot.png b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/img/sample-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..d7a56a3ecc078c38636698cefba33f86291dd178 GIT binary patch literal 18849 zcmeEu^S~#!E#4Tq;}?6chqwB{?k=6jc5D4>l%v(rleJ2Y%tW zDj9g7px}|*e;{M?LDwiK3@FNS(lDRTd-MJYIyUJCN948~OJk1M(DrJyI#iV;P4k~& zFZo35IfQt0RwlUN`48^6(1dv_wm(y1xhEdMld=Y?!%u=fPT_*{3( zwBwz3#qR}_)t>C*jp5@U)Ti~B)Y;qq*TRxZJ7ZRN_^A3TDAEM*@7Ve%(Ro7=1%1B< zVj6GBUTxXev>_^SFA zgKZ=g4aTS}9>Ofj7cSB0WO?gQ)x=+!hs_)b$6#>ScFZ>XAoIX)%Bc|BDC~JFBk0f0 z0NY}6gb)&!qx^FWC(!ji+Kl$V$2|ocA=vN0TM0Y`U?tX+T)c*C zA!IL(T2Vm%MCLa85^if@J@Kkprx8QN5!6eCR@4Oa5S?4-4|ou?90mFCM8D!;n(5xz zO}-*t!TntN>|a$s(kGQg1P-U?hqvGF2_fGvd&~yZ_l3Qf&j~XWa=;>N3#-~#zjzcc z*m18L`A-K2o!d@J>a8SRbm4P&-q1(H>|JgIymDbnJF&@008`=X!P?4DGgZb>voUl^ zNJKgPR4S={)3vuk_{n@=M8q;;aJL>q+VLdTnO=}`&x;1DKjJA3*f*idS{jP5?+;!W zn-^7021Z4zv`Aq`hmX1aid997RNh3fa-@PG(W7TzKa1W&5^y3|lPeETP7j9qXpo4)7%(W0_2 z^Nmq;t@rb1eP3?%kOkH`P%!zTC7ZHjSfNN3*Sb#=3#jB*KpNGNfnRZ{N(6DrW(;B2Bwom<%m?VQP%K+ zsFeF1-(DY}oP@)w^Kw~gPg03q?N;)Ec6^|nikA34T~RynX*z}H>R~qgT$`Zbhn8wzZs$j2fsGN&rOK-mIBBvzD@a8FgbLpL!h5N^u&0wG} zq!#md3MHITv?3@$37J?lc_5*LWJTTjel;IiU-Yq;(g9I^D&KN_NKVS0O~GvB~FzPM6}=4d%fG4Nw4pZshcyLqK@`b8?RhD38haIyr@+8+0r5TC1*C7^WleJ zZN3_ngTD#RQvNL*;qD2H@cBWJbCC#d!}=oKfod5SE9a?!?j%DVt1z@inN}Iy$r+96 zM@P?AC+(`cM;z6J94BYGJ;+P-N#yj$?`G26ydS&OVH?~JY(N4l()Fh+x+DoJ@r<+i zhm^ck@QP`=fLApr62@KyOef~}zuG;(VbDQmw|Wb+oSHSw=%w9R)=et0cY*~ytX)#M zEXlK^p;zM@vTnXn+C1vwP)~TJv|TvDE2($;;EzC5_5IL#H;u z)#CO8)TSzbt8)wHB8$I8KcIojx&GoE)3QNu{CQ+_xBmQ&`mL5-u=BX(hs^hMY^ zae!!*Q;Tr$@(0~GoBJAohGw*d{l8~!aXop87aaSUb2jm)Tk>#$1*cdo5Sl+?oD!l4Og~yX+soottl4 zp4OartUuAN(dD~yLJ}`A1*!D4-|L^hM;`_DM^1KYs-VF(}h(BjRO``b+xV~%O=-)?p z7ciJH7Fnl?V&=ay_AB{oQoa2iR;6$^tiE|-eRCFy|3F@%j#6gUxkZX@?K`F$u#;T< z4IZORpUthmB?U`;zrOkp?P(Rvd5TFRWrBJmVg;KEZvJ+;Q}FRY%QZ?c^&$oPXW+C5 zdN#c>v%U?QuE+hMQdzxS1Q(BT90;29qu#^A?a^)Ui;{TJ;%`nLgm2ew$J4NvREjCJ z$`C7&?tH$CrVG@M3J1-KJw_*9BKeL*JX{ zN+Vg_TXb9^jJO$ZGkXO6BBFDjt~w5`w2TB*z$&1W5Il3IiDs=ZMDt|9iRtKET*wF6 z0Z+|N87p-5Fh)^(*l>OVr5^aY5LW(@PuM>Qo@&)yj6XRkPm1>eTF#Y_c*aRF^ZY5A z9FAU7lKEHG@i{wJMPg;n6z2|69d-)q9@<7t()d-zPy&X zdXG7{Uw{k23)CzzQAXw#iqj<1u~W@K_Ljc#?ukh;fRKHeJ2l~Z+52b2n^bGiDF2oX zm25FLx|4AP8>rAi@koY03lrtS#X?zK591c?2iZ_jjc>0y>q9>fU<08o6zG%z9WK+S zDwZMW4~28wu#ye#V*@#5t^S@NiAA`3{SF$xINmc_WW^u-C9M=H>RQ1>WM=|R!660{ z6E6%DwX`eu<3pkmz7Z=FCRd$(vhDkc3yMnSr)5C*aho)DZ<12$`$TXj<8Z70)|rK7 zXFD8QzksfWZU`qL2K8X{C~TcF{KVW`3Y{IMb&)T9%1V`tv(HY1 z+LXkLyM|3mtLD{x-#hOw-U?sr-iLeHFA|=-sGZ4#hX)atL!a91(tWJc+og&5W}VfZ zpgE7`{5D`~?yGR++y7~xA&eU0N*ZezDjF$> zUeK&1aTFQRg*?v^Z2e7u<`lk$czR6}b6Cl-qA9%A`#A6q0*zyTu)X`3rhjR86NK3= zLdw{+-F}+b2gxd-qF7>Rla}dFkj|L#c|pg5Ni+MRA|BZH(@ME*o<1ijKcoXb%PVfJ ztp_uf=G%kvU((pHcw90Xut=}atA!giM-5By)f40nKp zv7Wdb{;^<}VRvruH~rYr~wEuYY2ov-5Q|p@u3Da9+z7PeIpBAwi?RxnxN3Kt+N9L(LUS%wxY` z>e&1VV;{CYw8DNRlvBH)>!I49SU4R!t3I4=y;mCevPZh!-}~G+F>6hcL_Rli4r zC4(WN)`j$>^S=~GMGR=^)A6wrqi(-x{xK37&Vx!OS6t=KQ2JVZo#GrSODtTe=TVh%*qfF%91nqsMNLNL^Gp|_ zz%I*HUkMQGqb!1eh{{bp|0GSCDbkG_D_d)8<(0r<6-%Qi7qDa7xZjcdZ$?Rth9L!f z$erCcs3<~mtupywbaT8NWZF#v?iZkvqSz3@p`RiXs7P!GUa~-U9hEG(NgI#3BzO-# z!9JWf(;r!*A=@g$f}>wi|6Q@9z8AmYf~x8G%sp>C5cfuJY;hs1o3Ozu^{pH0AFbs%yU)Xy5>Cf?qXiHn*-PAfKDRiy`U0sFSKFsgEZ6_ z9#ma!<#Izr^}_z*>PRSt564u6We*XmZUx^jv*dK; z4zyFZ*ZFSE!00<6!|+#33&R)@RA8V9YRjp$HS9?CGq*xDSDRbX#i;}mateEF{fqTI zt?X}Efkq_Ap*_ETgaikOBbQ|;47}hwX44K`(DUI@C)QiG&6UJ1UmRn*Q@6%e`+x(gpQp74O{;yli8YLCV}qD z4gIyZd_(8ED~WWaeXOb0^r=9=AiDT}by~+$KVF~M{ywbQl zng-h?a_E;yX?DCr4|_h7JMc7>xgWf7Ek-VmH^hCYunVp3{(d{---&%-GZ=rK#V5Jo zJvP8b!2AA5?9)G8gwzB6ze3TU<5*Pqms^Q-?C9-CN~4hb-`U0D@kAkTWn23``cao^ z8IWAp8h7`%ZA+eI?w$sJktq5m>e&0@mQn>2BdpKAxbj1$m$8Z;`!iFvl9($Lb9Ff? zT^6cTZ~HgIeR6R*;G(rzpgsJP41Fx9Df;G6{;k6T(i}&8hX(jHSC@~#X@70h#)g(( z*9vUC+a*b%oAdf1$}Z3NR;|c5nY4^Z51pfqk(tmJbB;Q#ka#tf5eae;-kq$I{xO3<(TI$0lSe-JQzJ*es;il=Kn_?&?E zfLbs{qErPqm)-*ZfwbA*D-shgb|1;X;cH*yA|q8gS=HiosF=-kbdk6--SR+`F^H_` z0*i`J==@XSe=HT;_``G}ulE=H@*3GU*?gVd@h*`eT^GKjI;C@8+h~;(u3bA#b&bN{ zYw>dJ$(;RfHDLlndS`CWOE=g0jOocCc&;w(dOzrLf4-DK*MD@P_;u&CbfMw=#Q-B` zDq8hGwKN-O7(hQA_bP3f5XrZH+@*FGw~ppmDgNWcf|Lf*Pc%e5dw1DcJ1BWm!z7z3 zr^toEU*P(>G#;_1X}Rz(5lbDtCui%hY^d3lm)kw0vyk zX~K4$AG#7cG`6s2%9g9zsaQ9o?;3yzW4Pt!;NlS zzI#G7tiq&@eV&}qDtY(e$1JwscAfle%Al{3>Nr%``n?`Jac^CdOXUbFgI3;m{RkA~ zokl+lxuw9=%W&MmzA+G%ZdFMMP&N2^6BWjG2Lt|xKx)lMCR@b0n+xgw<)&Dwi?}>- z+$_e|@M;uW@3z6)q&L7bYitZ%huzGqH_qHOr&G5o!?(8TJv_MN1ka|&c6_!Q>#PgHSFoPWiLg|k_{ zQd#Zy&BPkU(0OE5S35!B5qb6%T3Wd#J(zBl8dw6I#xIDDF-LBPi-jXv1E?!gE|1OIdTejK)+U3ooC^otSIRsWZf-`&K}6}s!407Y58zH zK(oYx*7sN1O|Z_1YIJS_H$E@DH(hB4QKNCGQT3PTvwYoe2&8WKi5`5tU-r4!>_V3XUT}N)>8V;+z-!@-IGCKiD>E9RC(K`NMx=;Qp zf$2g^t?)zpU0L!BZi(oE#)^Z_biT*Svh>r#%1=O+Wo37G`Q)4@k#Pe?^mgBIugC)8 zyEICH=`{A~^x#X&%tr-$j|(nXrIrGQYNY+C3M+LO;yUU4-|v>a5#P)XYp>_|C0f0n{_p0mvwWmghfd%!Cm}$qBDxOqA3htLs~ghSA1>6^dVgd~ zVHHBBy6;Pp=El;dkTE=ttp~BoOJ$L@EB3Z37T1kTNG3tm4PY5O-7hP5DA$-k=vV&6 z?RiAm;W~*o)R7!x9>u$&@|&D4xMmJ*y+^-6t!F0u8G~78t&Bs#W>w_NbW>W9M3tXWXRf zI86FWVx%iXXh6MJ>dg#?lNu{K@S#nzMIG4PXQd%!Bvc*H0c7F_Y=adptJr*cHevMQ z%?Xu~q8CFw>^L*S_83kVhq=)hf0%_Lq}SE*g(Da_A{kXVZfAd*YCwp~bG32wi&SNM z#QZ7}Ug5-=+s^uqAh_|}gzya<(&E?XAZ%0ybd9nraj?|z1YfPr*{N?Q{ji}YG`T#| z=uwJZHIMlsmevnenT#-)t$L*=2wh|1EYXW?_36TR?L!sUItJVxaC0$Gb|gq4{|4gA z(v0ODFj!T)jc5>65ys)* z7$aBHfbKdz@QJq1b`NT`344*g()$>5*Ey`TPB7WI;|_8o8t9-_4ikFub|I{66>ge> zHA+6onzFKY*eaiA!77SD*^&LyumAR6gSvxY6Q?;!AvI{rZ##!G$%ZfIgce4F`aF;e z?jVh%+B-vj69ei~bh_zA9w}S4B4rzRKQ1~u$gwVu_x5PlRKDXX2(_2Mm7fs%6{SS7Qh1gWT8xaxc=f8`mW38ukIZxwU;lmHABwFSg50*o zrj%f%j~IKR?N5Dxwrq|sTa?!pd{b3sFM&~{4~_^YH4$bI^Fq2W4-y`))^|7fS?i0) zJ&Z9wY!8%l7@gAr`2{fqA;L;ptQR*X2|xUtrT47KK%XN+dydN$*M?65LuXTRabgERR{n>;E;(&vS0_@COY!p<%5LsRqGpER%~YjkSK zwBo9-2|-ZFiU3TT&S+@}3gDT35t0IXTzX@yHA(v>Y8;-mZNySQ&fE7RJ1^tzJfvdApX& z*!+tE)Y{oR%jk8A)3EiI3i*(TOwP!;B3hAOj?KQ6^h-q~1V^166uYS~mH*2Hh*0}r z`R3u1#^LG9IW|^QT^|61H(T1Jz?n;(Z>52lU0BO>Q6*zgpP*gTFk2Uw)!3zt>3F~_ ztil4!R*-j}wjh%&(kSB%}X=u4RbFRp@^l+$SmM@nW9B;yGbf@nasjFMEE{m9Oe

}qal5$moSACwfNXLXG5|3R0AtBcN` z?%yS)&>O>sqxU64U~C3&Q^>z-Zt}WuX4Wh3dKj9EO zfSbV!c3e;EOeKHQmWEw#NM4;*tw-2o@x&kKT?rsmy-F|$jw-F>WgA7?C@{O1qPg*J zf92|RTBMh&ptHADFc{T+cB?+mOj>h2HKgwkxq6w&XBxPc?>=JKvU2K9aU93@vp-R% z{5T=P$9U}AYZ5QU{3%7}YZ+ACWXw#-U zWyxU(OP#Q9-2AeGmCwcp`zWghf2hvsOjWjDQbU?U`v0&a--f1`v0Bd8HLiLmo)PKz5!A1|XVO+89 zm3h2~6yI~cpWor!_yt-?Lt>z`c0a7cJAW)#d8N8nNIf0H<+v;s4{0guDD(?T7Z<~$ zd`$vpZ_QQgFaMT0_d5&+(jwGU?M1FqUu6wjA-9z?mRM}(CmSdK;2e$Na}F-8jbhgN z9)@AIQeghf{xCC^{9P%VdYW1PP#}2BJwWt z0Hd8%st1NK5%h+)UB^mVwh{e#8TIm$xxgGo6I5;e{~VUeeMGRpM_Z%=eH5$X1}?Z5 z`|*_Vp~K&ziz45-Ih9y>EOr(Buy0&n$dbQ4$5eSr=Ti z#~7^n8dmem;$0D4+6eV7&G2D~d@ z+R#u8+nw_N%7_U_1e53P?~&10^m|ZUXrZhVp04lQLsGos%0fRDhS=@>8TOAAxK;Cy z9GZw_1pfSxD5~xoR!INI?tU0wrKDd6^Tv{jL>`Xb49kBaNPlhMaIfh_nq_)zB7NcX z05XeQKz`@BDUx7*i!V~%dc8XQ#ngBw0A2tSr(npSCrNy5Z7>48v&Zz?0{%FRElh_h zN2|?#EhJL5HQMIu6m1=ypTR?tVymHK)xQvS9ir7FzMp?CjlND39PK`od#GytVhZWp zQ1@>MTE1*Ip>hnXSWa?XbMH#708@j12yPbm`JfcqIgmJepn$5YgkJn_%5I)mr`Q(k z-a0yFR3A`houhvf&|wNpIsV{2p%MqhR@`@R(l6`}iufEgI*UxWq~26?WTpZCV{JtG zYL?&#I98fyf_;2S0?_V{=Aa4t^x%vy$pF$_Lh7W2f*~5uPvGYh;vZhMv|u+Z?2t0~ zcYPXdxbg6OS*LUjR_=jLDt)ab6;?g1IuySLG@UE;jLpt-wjLX&RlY>fnd@f&?0NyT zht5vhP^};k6`U76$%&I)iWPNxG6KPjdh`S6>g9GN@;KObQsLG zKyjfrPR0PU1B0a0=)3@9eCDl?mB9rFdlTMtTAeZv2}F*|@JWleq2+H1bt>>x!^wTk z+I)cgsZwzCMwoRpW_*!3IySTQu!`HWugAXe(Ai(a9Rsu;*0#o6torxwNMxPzEAjt` z>70Vw;HCQ?AnP`RKQ;2R8h%;LI#tx^(MO*lMWJe4_?)Q571P`kTmN#(ez21V!<6+S z@Uap+y%#8&cGgdf+E@y$dUx3g#)=#5k31Vqv0p!%L`*=-PiQAiSg-d9lKRZQDuJ-| zA96zwwomG+4}X$vR*IU=NC!vL<`rUTbf_uRJC4FS;k&HtV<=<)p(qymH)=MDV^aqK z#%sid7K|~!H`J!7hRr~Z!emxgWq6#GpQs%c#BM+scvNGz|Gi4G`;8Z~dP8)+51iB8 zw)0fazNz5(iK$LJeC_4e^8&@wT(DZ~~>SStz3P(>V8CLNlZqgv=2K-|Lu~si@XFwMN>QE^k zVS2U_A?Q$?M`NkU}^!M8m%O&T=kW>dG}1s2I~hxp9Y=a=1XX-(fB5) zej3`e5Et~R^r%?CZK0)UZsF_+tSOGIBMdrtMf#oJjGF9U`*P8t>i*TWed$Z2WNUZ* z_1Qw4Yr+Q0@bD?hD0P-^v}?FpPBg~zz5~g@J#J76C695|P>1l;OS8%~hZh5&-9Ji# z50%&56ZK4FC9}{jHL0!=qo9Yd(GGHCEX2|-F(f}q6@NMT4P3rQd{Q!=bz-8N(Z^!N;;ZzAWRf@C?X>mG=_NgyQX_?Jv$m(9$W>P;+e}O|&w&DjbsJPdWp0A2$yLr*!BY73Z z5d*BCaTI)w=sTlofc>n}@v_tSXIK?8(g`G_06u>SD*fOZJ~visq3lBVS2+cf-r$UQ zZ(8A0g&5M$IV7w5nqL(m$VS0X?=yy-e6>S>Ca3wZNT)b{GF39_gJdONflqc-j$b~o z2l@@h{$KVfC)V?#We*)@xYC;L^<@cHo>8axRMbSzw|eYTl|8pkabsQJ(3`z{>5H}c z`psz_Y6t)hvzL^=}P#++XUl6v`-j)SuXd6BynjNZ!&c2hnyE&4*K$nXn31Zk)cm+lx;> zya{T?{MRtSu?^3Y9bS&O$*mW^vRUpv!J3Tz12?3&Y62b_oiZ$24O(75Z)JWb+Rj)ACbK`f<&tSwtT$|Sy z$41kRPiM-jnPY9PKrLyI`pHm6LusMsrO*HpmE){Kp1^u2t%6nW^;GB|!4k!Ik8oav zjM?DBKh9G@W0gEwiU-M}0B)}olvoM71RccgiZBCs)L?q_GX&JDhegx4k2&cNatr5w zU)1#2USb8&`etO5Vk z?0}K+*2*@a5yt*X{qg0@8jEz~jcylVj>-042p1PBnabI#xUiCRD!ouw3?u-wwsqwF z8(@m8-Lk7q@v154g6yvx_tRDa>}oqpVda)wfI9(;ZVGt1v^{<|X?vC_(i@IJC+2I_lusrT=$h zF1lPc*Neb`;Xgrdf`p$w)~MzQW0M3_FYRKu{2$VU82J^B=X1#^<&P$_`=S$Ey04WU zTxG;hrFNLhWC*p+sH3x=JVcBJ9*7>eO20)n671SxQhZQlHMRP8FyO}yai~OTsbms0 zQ3b$C1Cn!>jMHDq{VX1ab^~_Q!z+f75+_AuwiN0*wA_#M#0|rU{+NlB%>Y+TNT0Gj z`3^LKMSJjz2(?lwg~ixDl_5%rzzZ}o_6Fj9e)T7gpH4=BgT1zmwJpC@g(f%&0`}8B z%7Y&qlP3aFmI#nmT`|R3+Lwzp+PLXt|5g%vlY_$fvse7zjus0D0fA##r+i4G4K-2Y zC#H95NGoYfWP#ZF_v$^Li{PZpm}fc&)aL?5doPcb835Cr6`T+EzzcEvLtmXcbAb<^ zw!_Zgk6Az7YA@*vb)(G{_W-B|zrf76z^`X%jOgqIIaqi~5nUup3vugzzg&rA^w(zR z+qCzvIV~nGR=47pDOcNTzuBw#5a=<=DMvGa)g zPw$^pmq9Fg&b#BZrPSoml(149rZS!fioV*Dy$z440U3MXDJmI?RZqLy0}IKSxN)o( z8+8wIZs#q(|KTg6y;Z(=96>xfpUsr@SP}I^v zN^R;ZVrDaWmNrM5-<X@k6JyjvA3;jHhma|Y|7!Vk& zgf(UK_6~cC;!|b!YTjke=nBiUqQdb#I9TY}!s5P)H+^c;9cW(QO8O%n5J^8Xfktd*qrn)+?-gP`m%B&q zi^}7jKm`yMW8ITFOMN#!QIB6$SWx*75tnCMaNg*_J*WuwBh~AT>0($nS8%&zmFQDp z$dL65niDtTV%!Kg1`6epWoQGNG`$`doy;Zjaa`keyL0F6iJMae6FIgnhAfzU%m@V+ zm5rQihLwS~b6{-bVR1ZSzBI7(Yj+V6T-8V*7I`ptWArGdy~8pnV>fALpi~NQLZ7;^ zpaj35=md<~-(tNmF69UX3?ua}A7UIn)q5i1iPYEGlhYSbkfeX`5epkxtzk3Qbu| zlgA`7ts%IvF4HJ}-98akyRnjCo{u-`A4&b+r?s|o`4wdYAHs-yh91p$7C_|+EdYH5 z10`!*=n+W9g>V&dfU1H!J}ASZi&-?`2IlDOAHnu306rD`y>jT)4^@S(X4XhN2{g9i zj-ym98+RT|d0ejIFJCM5>S{mT-8uGmRRqkJ3sMO_AQDrv77Q zv$t>zaVpVF6eBguE%9M2u?E-Oleft8z5+~W`G}KXD(Yc;7m4{Op>Le(k`g1UK7(1# zt6g}$n=Tdn{T4pu>v!c;xRCd_WI$Ali13x=U_0T!Ga-U~9W88q-lU+RLn2`N8Ouho z^0@SvC>$DguHWx)?^*ms-{PVq%dn(U3vrLj9zITDqQZ`H>Wsp@Gf%}SG=m)Vh}F$ztQAbwVGdDgd!28j&yX9wLW&s! zNR~6`nYg;ULAq8zi<;gUchAV5ib67Y##l2 zy+%gaD(|~G4@||{A;TYDSoS>q2o{t23t-^!NDSDEm8j3ao7Ei>KYLEpb$jz}7ciAM zD}trDN+AVVT_lXW<++~>8>Cj8fzJo@R;>%nGq)6+w?(#mNc#1J4W+!hA}?g$0Xqo? zn67qJmss)e%k(xO*&K@z6+}nHA(lCkb6n-|{pSztys$8HiOWTVR)tCO*Q9~if%3n7`uxGzE+OCu zwcVV|tgQdq60952$>85-GHk$lwM(uI+CU1?i{sVnKd0+UNq#eSSKjUKfDDgLnBG1y z^v?f#MRFkph~TgkoKBvM`L_~we8__xpLcjh`GwV|87q`vazJq?SX=mXhdvK>VqUf~ z4sYoTIpt5S)KrE-?>&=cRoBumD7;b5pq!Y07)#I$`)<@U+mo*dE*P~773p*u^6waO z2#thJahX_ySlYMpjx%h<)i43ao~Is`^Ya zMNZkuChEA7+ZJe6$>-C*dzTYf3#1SY82yFG?S&Q)5rTbKS-XLjckTLEc7>^sFcntQ zBeNXCSg&q1N3Bi^4zlQ%mcEBQ%2ab$?(;t-$HYd2%cnX$uuwU#I_6D3($m zR(>gHzM9ODf;r8b0l5LuEIQVZiQ0-|3Y_xzJkZc*CD=bPJ+&J+>>se%D4uTq?Ny{l z0Z5~og*Wa1O&anlcRWu_%o)(x?IZ0CfUNk_R-ik>GyvdFmpu1wHZaKTDGhL zqxsji)n<+)VKbV0_BRq9E;Kb`f=&vn(BK0Ba-gL?ZN;^^b3YFg6R=!q#zM;tcX0dM zdy5PPx@6pJPXHzH7$dGjM|6@6777nXPWV;CIQdNf(*Znv)sMy&Xcq> zhCq+6h6&v8<0}vd2(sKqU3j>fr7&#Xy%qZHcMU3m{wld^Nstkz8GagB?Y=SI&H z&{&BSA-|(i35$9(l6LpFyLm$0M0fK`Dz!~ezL?yEInsXAFR!bHe;ZL>Gd(#Hv?<$%`^b)oi?x%(jkylCPb=juPlF znMo&o961=NZ_$gd{xp1ZY2dNDOS!=XVj!M^A z+$z`EK4v=m{Bs{&I4W)({`&<5*^BV#z{IBAI_d+9Qx;~ zby?2zEjzUUeZWBDo5cz>%;z||z)<+6UtC)y60yD5J5`oo_zSM;l21@CY<0_|)NME5 zs)kHCMBa5YzB#N=W2aR?y9((~WuYwwf+HAc2mvU>NYlxOTvGf^Ye3za?*f-qUs^`a zT3>RPh9*Jf%3*bf|kqtnD_Buxv!<9N>BbuD#uYv-q^ z%RDnd7a3O4M9Y~TNISS@9K}JDkdg@>x8E6@n8jF=6qiDV+}{!V)(o?ykcr0sxBGEx zo!X;pc=r{H^vw6ztV5VZXBa4~(ujB$rZQ|AaGN@J7#q%2nU9gJ)g6dcj}zYB1& z@iFE0vMQVxa|v7tDHS$gwX$Ihc#M^DXRC>J@Zk?dC(3uB_s~*W&m-01DFMQGWjj5x z5po1@1gPl!v1Yra@qPG{D;$bYLM3qOwpl~7f~l)#n< zP+6`!NYe3EE~4RFR#_e=7YctPRBt6$He@`%e5m}f$M%yzC2S0<1}hRPjO>HJY~ z*dx(nbMbjv*;o&k{qzBdF|lS;UNVKziV=gbLq}UOCwr8GT5E9oRYQ}+>DhbQ1R=lj zgcNJN8|D)$Mx3#c+t@lhqcDUnHGVt0&EyQ{b5)=52B(VTzw=pQ^ba3`JB@BU^lS`_ zJEiLzgU#Acd_!}FMxCWC**FP^i#P}bYzNs78)#uSejEtYLbG>JJ7Igtho2oKQ;XW~ z4eMGO+t!_;G^V6c&R`5Tg+Pz2ToN(aybq4Q0ssie_{`t*DO%V7FaZ`{MBobFc9|pV z70o5ayHGJo9$$&Pgbs)pWNzduAcbh?~U?_P)(ve0S*3H%eNF&a5XR=!J#4c z;t992n7ZJr{*%`^dU1d-ALE8!3i#v;3r4r%j+JFCe=%3Vj=8{aXe zs)jrcUBZ=;LudcTUXj2ub>K5!{HHFHJ}Trx(PYugbQ8yK7&sqX;(;|UWjk3tGs3zuceeX)i4i_jA8Qz2Bc%DxN8 zXw!$+9jBtEHd1y90bYG4f8DcJM)Ab!M39tH5zz94*MAvnhA377@buNupSOUU3j8~> zd6&hk^ENRCp9T?_QUHk<=(&9Q^MJ^pi;nKOYNR@?L=RCSmKMJ5UQJQ`X!i~(gD*P! zs`RobzJG3Ra_Pg+WZUXUmMU$ilpwfcEti6)mw(~MZ0q!^sza>#jv!-+7B6F3QuMWg zVO!rXwD+lF1BBTito?ml-CV3vxuek~TKuOX^N6sol$v*{_%nAuD7i81eXm^Lz(Z~I z2Xj_Dts#G0&C;PV_Wkq*1QvB7+Post4={v;gk7b9u%#DC_bh(iJm$rqog^{JEx6NE zrs5^2SEL$|98#2WV#iG@L6cq|)SuTMSfGocPl65wUd^|5Lbpnb(;t>-Qu2jvANLgv zdte0vED-3C@^BdyHWLL(7{G$WA02z@JG!T-U^Q7HZ(7Bs&vchkh(p&}KvnS{MG^i6 z4r){gJp9p7WyWOEiKA2Cm6EXIn&&gk|Fc6^78OpPrX4ExCFE=SD$xcH;C2eB^{XTI zaxz_Cef*Yj==w_i_BTGXP;8C&f? z*QEM>={jFM8)lWAR870pG4XEWsl%%K|82S5b=9hVz7p_6i-d(Iyvq76&a#PV zR;VbQV|n?mg}&(ehClg%tK%IjgtnTR-u)lxH06XxXqH0soAZbB_Rm)XX=6Nge1uoG7 z9vQM_S~2h53n|W`y{{R9+=08rv~MohI_v4-BU^7fZ0-A}#b5{AOSTJm+(J;9yw%pD zX6u62GJ&@HKX5zQwq~j8T!Hrv-Mk^QSB5cu09L03{ToDO7jikM0WAcsjW>D}^jqCF zT0DEZ@K^KO_MD*%M!+V)lGVU6?LpX)eQVXEmq}R`NIJv;kBitJ!nW?0OxTVlu2ADf zE{A!*0g3%nwVcBD+AgT5bGx@WOnQk{zRpiZ4HhP`3BF%N|HdqPbbiV5)7x)kzC3ID zZ;27>0^mrMgWc7evsbQY`l`l})wr+e;=8U_!2&B77;1qL!N8y)eTJ2lf#CvhR~!Qa mc;sM|90DP5A*JW%f2r=u1xt!e4gwD_V(@hJb6Mw<&;$SznOm^{ literal 0 HcmV?d00001 diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/manifest.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/manifest.yml new file mode 100644 index 0000000000000..37452fc08a02f --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/integration_to_input/3.0.0/manifest.yml @@ -0,0 +1,45 @@ +format_version: 1.0.0 +name: integration_to_input +title: Custom Logs +description: >- + This package goes from an integration package to an input package +type: input +version: 3.0.0 +license: basic +categories: + - custom +policy_templates: + - name: logs + type: logs + title: Custom log file + description: Collect your custom log files. + input: logfile + template_path: input.yml.hbs + vars: + - name: paths + type: text + title: Paths + multi: true + required: true + show_user: true + - name: tags + type: text + title: Tags + multi: true + required: false + show_user: false + - name: ignore_older + type: text + title: Ignore events older than + required: false + default: 72h +icons: + - src: "/img/sample-logo.svg" + type: "image/svg+xml" +screenshots: + - src: "/img/sample-screenshot.png" + title: "Sample screenshot" + size: "600x600" + type: "image/png" +owner: + github: elastic/integrations \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts b/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts index bad75e9746e6a..11f315237024f 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/upgrade.ts @@ -9,16 +9,21 @@ import { UpgradePackagePolicyDryRunResponse, UpgradePackagePolicyResponse, } from '@kbn/fleet-plugin/common/types'; +import { sortBy } from 'lodash'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; import { setupFleetAndAgents } from '../agents/services'; +const expectIdArraysEqual = (arr1: any[], arr2: any[]) => { + expect(sortBy(arr1, 'id')).to.eql(sortBy(arr2, 'id')); +}; + export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); - + const es = getService('es'); function withTestPackage(name: string, version: string) { const pkgRoute = `/api/fleet/epm/packages/${name}/${version}`; before(async function () { @@ -30,6 +35,25 @@ export default function (providerContext: FtrProviderContext) { }); } + const getInstallationSavedObject = async (name: string, version: string) => { + const res = await supertest.get(`/api/fleet/epm/packages/${name}-${version}`).expect(200); + return res.body.item.savedObject.attributes; + }; + + const getComponentTemplate = async (name: string) => { + try { + const { component_templates: templates } = await es.cluster.getComponentTemplate({ name }); + + return templates?.[0] || null; + } catch (e) { + if (e.statusCode === 404) { + return null; + } + + throw e; + } + }; + describe('Package Policy - upgrade', async function () { skipIfNoDockerRegistry(providerContext); let agentPolicyId: string; @@ -1226,5 +1250,118 @@ export default function (providerContext: FtrProviderContext) { }); }); }); + + describe('when upgrading from an integration package to an input package where no required variable has been added', function () { + withTestPackage('integration_to_input', '3.0.0'); + const POLICY_COUNT = 5; + let packagePolicyIds: string[] = []; + let expectedAssets: Array<{ type: string; id: string }> = []; + beforeEach(async function () { + packagePolicyIds = []; + expectedAssets = []; + const { body: agentPolicyResponse } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'Another Test policy', + namespace: 'default', + }) + .expect(200); + + agentPolicyId = agentPolicyResponse.item.id; + + const createPackagePolicy = async (id: string) => { + const { body: packagePolicyResponse } = await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + policy_id: agentPolicyId, + package: { + name: 'integration_to_input', + version: '1.0.0', + }, + name: 'integration_to_input-' + id, + description: '', + namespace: 'default', + inputs: { + 'logs-logfile': { + enabled: true, + streams: { + 'integration_to_input.log': { + enabled: true, + vars: { + paths: ['/tmp/test.log'], + 'data_stream.dataset': 'somedataset' + id, + custom: '', + }, + }, + }, + }, + }, + }); + + packagePolicyIds.push(packagePolicyResponse.item.id); + expectedAssets.push( + { id: `logs-somedataset${id}-3.0.0`, type: 'ingest_pipeline' }, + { id: `logs-somedataset${id}`, type: 'index_template' }, + { id: `logs-somedataset${id}@package`, type: 'component_template' }, + { id: `logs-somedataset${id}@custom`, type: 'component_template' } + ); + }; + + await Promise.all( + new Array(POLICY_COUNT).fill(0).map((_, i) => createPackagePolicy(i.toString())) + ); + }); + + afterEach(async function () { + await supertest + .post(`/api/fleet/package_policies/delete`) + .set('kbn-xsrf', 'xxxx') + .send({ packagePolicyIds }) + .expect(200); + + await supertest + .post('/api/fleet/agent_policies/delete') + .set('kbn-xsrf', 'xxxx') + .send({ agentPolicyId }) + .expect(200); + }); + + describe('dry run', function () { + it('returns a diff with no errors', async function () { + const { body }: { body: UpgradePackagePolicyDryRunResponse } = await supertest + .post(`/api/fleet/package_policies/upgrade/dryrun`) + .set('kbn-xsrf', 'xxxx') + .send({ + packagePolicyIds, + }) + .expect(200); + expect(body[0].hasErrors).to.be(false); + }); + }); + + describe('upgrade', function () { + it('upgrades the package policy and creates the correct templates', async function () { + await supertest + .post(`/api/fleet/package_policies/upgrade`) + .set('kbn-xsrf', 'xxxx') + .send({ + packagePolicyIds, + }) + .expect(200); + + const installation = await getInstallationSavedObject('integration_to_input', '3.0.0'); + expectIdArraysEqual(installation.installed_es, expectedAssets); + + for (const expectedAsset of expectedAssets) { + if (expectedAsset.type === 'component_template') { + const componentTemplate = await getComponentTemplate(expectedAsset.id); + expect(componentTemplate).not.to.be(null); + } + } + }); + }); + }); }); } From 15d558c36b0d0be096c5c555dcc025749176cebd Mon Sep 17 00:00:00 2001 From: Lola Date: Wed, 15 Feb 2023 11:08:04 -0500 Subject: [PATCH 30/68] [Cloud Posture][Benchmark] add avatar to username in benchmark table (#150824) ## Summary Summarize your PR. If it involves visual changes include a screenshot or gif. Issue [139](https://github.com/elastic/kibana/issues/151141) Render the `CreatedBy` cell with the user avatar and username image --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/pages/benchmarks/benchmarks_table.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx index ed276dc374744..dcef8163f028b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx @@ -12,6 +12,8 @@ import { type Pagination, type CriteriaWithPagination, EuiLink, + EuiToolTip, + EuiAvatar, } from '@elastic/eui'; import React from 'react'; import { generatePath } from 'react-router-dom'; @@ -144,6 +146,15 @@ const BENCHMARKS_TABLE_COLUMNS: Array> = [ truncateText: true, sortable: true, 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.CREATED_BY, + render: (createdBy: Benchmark['package_policy']['created_by']) => { + return ( + + + {createdBy} + + + ); + }, }, { field: 'package_policy.created_at', From 5b0b09b59b81a96ebe5719b48cdf492e642fcdde Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 15 Feb 2023 17:13:27 +0100 Subject: [PATCH 31/68] [Saved Objects] Remove deprecation notices from server SO types (#150839) ## Summary After merging https://github.com/elastic/kibana/pull/148979 there are a few interfaces in the server-side code that export a reference to a deprecated SO type. In this PR we fix them by updating a few imports to use the non-deprecated SO type. Additionally, we resolve a potential circular reference issue between `core-saved-objects-server` and `core-saved-objects-api-server` by "moving" saved objects and related types exports to `core-saved-objects-api-server` --- .../saved-objects/core-saved-objects-api-server/index.ts | 8 ++++++++ .../core-saved-objects-api-server/src/apis/base.ts | 2 +- .../core-saved-objects-api-server/src/apis/bulk_create.ts | 6 ++---- .../core-saved-objects-api-server/src/apis/create.ts | 2 +- .../core-saved-objects-api-server/src/apis/find.ts | 2 +- .../core-saved-objects-api-server/src/apis/resolve.ts | 2 +- .../core-saved-objects-api-server/src/apis/update.ts | 2 +- .../src/saved_objects_client.ts | 2 +- .../core/saved-objects/core-saved-objects-server/index.ts | 3 ++- 9 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/core/saved-objects/core-saved-objects-api-server/index.ts b/packages/core/saved-objects/core-saved-objects-api-server/index.ts index 467b5891ffb85..812ae5376d775 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/index.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/index.ts @@ -60,3 +60,11 @@ export type { SavedObjectsBulkDeleteStatus, SavedObjectsBulkDeleteResponse, } from './src/apis'; + +export type { + SavedObject, + SavedObjectAttribute, + SavedObjectAttributes, + SavedObjectAttributeSingle, + SavedObjectReference, +} from '@kbn/core-saved-objects-common/src/server_types'; diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts index eb80df8d96d44..08cc7580278cd 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SavedObject } from '@kbn/core-saved-objects-common'; +import type { SavedObject } from '../..'; /** * Base options used by most of the savedObject APIs. diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts index 7a38a909155ff..9e945990141e1 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts @@ -6,10 +6,8 @@ * Side Public License, v 1. */ -import type { - SavedObjectReference, - SavedObjectsMigrationVersion, -} from '@kbn/core-saved-objects-common'; +import type { SavedObjectsMigrationVersion } from '@kbn/core-saved-objects-common'; +import type { SavedObjectReference } from '../..'; /** * Object parameters for the bulk create operation diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts index f26e75bd37865..cf472754acaee 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts @@ -7,7 +7,7 @@ */ import type { SavedObjectsMigrationVersion } from '@kbn/core-saved-objects-common'; -import type { SavedObjectReference } from '@kbn/core-saved-objects-common'; +import type { SavedObjectReference } from '../..'; import type { MutatingOperationRefreshSetting, SavedObjectsBaseOptions } from './base'; /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts index 5c57043754161..311be05126a76 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts @@ -11,7 +11,7 @@ import type { AggregationsAggregationContainer, SortResults, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { SavedObject } from '@kbn/core-saved-objects-common'; +import type { SavedObject } from '../..'; type KueryNode = any; diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts index 9e764053392a5..b15075a67aa0c 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SavedObject } from '@kbn/core-saved-objects-common'; +import type { SavedObject } from '../..'; /** * diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts index db494d9d8d7a7..66f7103102b6e 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SavedObjectReference, SavedObject } from '@kbn/core-saved-objects-common'; +import type { SavedObject, SavedObjectReference } from '../..'; import type { MutatingOperationRefreshSetting, SavedObjectsBaseOptions } from './base'; /** diff --git a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts index a7ba3f2818971..bdc3f3950f8cc 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SavedObject } from '@kbn/core-saved-objects-common'; +import type { SavedObject } from '..'; import type { SavedObjectsBaseOptions, SavedObjectsFindOptions, diff --git a/packages/core/saved-objects/core-saved-objects-server/index.ts b/packages/core/saved-objects/core-saved-objects-server/index.ts index e3a56f4ce0529..d3d2ffe71aa34 100644 --- a/packages/core/saved-objects/core-saved-objects-server/index.ts +++ b/packages/core/saved-objects/core-saved-objects-server/index.ts @@ -122,10 +122,11 @@ export type { SavedObjectModelTransformationResult, } from './src/model_version'; +// We re-export the SavedObject types here for convenience. export type { SavedObject, SavedObjectAttribute, SavedObjectAttributes, SavedObjectAttributeSingle, SavedObjectReference, -} from '@kbn/core-saved-objects-common/src/server_types'; +} from '@kbn/core-saved-objects-api-server'; From ab3ecbd4874835064aaf305ee687945ea18f68b4 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 15 Feb 2023 17:23:43 +0100 Subject: [PATCH 32/68] [Files] Add missing interface (#151323) ## Summary Part of https://github.com/elastic/kibana/pull/146284 it looks like the `HttpEndpointDefinition` interface was accidentally deleted. Due to the transient/cross file nature of `.d.ts` file this was not marked as an issue by TS. --- packages/shared-ux/file/types/index.d.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/shared-ux/file/types/index.d.ts b/packages/shared-ux/file/types/index.d.ts index ff9d83b2cfddc..9d63635179ca5 100644 --- a/packages/shared-ux/file/types/index.d.ts +++ b/packages/shared-ux/file/types/index.d.ts @@ -23,6 +23,19 @@ export type FileStatus = 'AWAITING_UPLOAD' | 'UPLOADING' | 'READY' | 'UPLOAD_ERR */ export type FileCompression = 'br' | 'gzip' | 'deflate' | 'none'; +/** Definition for an endpoint that the File's service will generate */ +interface HttpEndpointDefinition { + /** + * Specify the tags for this endpoint. + * + * @example + * // This will enable access control to this endpoint for users that can access "myApp" only. + * { tags: ['access:myApp'] } + * + */ + tags: string[]; +} + /** * File metadata fields are defined per the ECS specification: * From 82b44b6399189542a9b50b57506eaef8d1ca774b Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Wed, 15 Feb 2023 17:33:03 +0100 Subject: [PATCH 33/68] [Infrastructure UI] Fix condition to show k8s dashboard link (#151279) ## Summary closes #151273 This PR fixes a bug when the Kubernetes Dashboards link is displayed. It will only show when Kubernetes Pods is selected ![out](https://user-images.githubusercontent.com/2767137/219019027-2b6624e9-2396-4950-b34e-5090ff170577.gif) ### How to test it - Connect your local kibana to an oblt cluster - Go to Observability > Inventory UI - Change "Show" and select Kubernetes Pods to see the link and any other option to hide it. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/bottom_drawer.tsx | 26 ++++++++++++------- .../inventory_view/components/layout.tsx | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx index 4ebaeb0d874b7..bb3a92d1e04ea 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiPanel } from '@elastic/eu import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useUiTracker } from '@kbn/observability-plugin/public'; import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { InventoryItemType } from '../../../../../common/inventory_models/types'; import { TryItButton } from '../../../../components/try_it_button'; import { useWaffleOptionsContext } from '../hooks/use_waffle_options'; import { InfraFormatter } from '../../../../lib/lib'; @@ -27,6 +28,7 @@ interface Props { interval: string; formatter: InfraFormatter; view: string; + nodeType: InventoryItemType; } const LOCAL_STORAGE_KEY = 'inventoryUI:k8sDashboardClicked'; @@ -57,7 +59,7 @@ const KubernetesButton = () => { /> ); }; -export const BottomDrawer = ({ interval, formatter, view }: Props) => { +export const BottomDrawer = ({ interval, formatter, view, nodeType }: Props) => { const { timelineOpen, changeTimelineOpen } = useWaffleOptionsContext(); const [isOpen, setIsOpen] = useState(Boolean(timelineOpen)); @@ -73,11 +75,15 @@ export const BottomDrawer = ({ interval, formatter, view }: Props) => { changeTimelineOpen(!isOpen); }, [isOpen, trackDrawerOpen, changeTimelineOpen]); - return view === 'table' ? ( - - - - ) : ( + if (view === 'table') { + return nodeType === 'pod' ? ( + + + + ) : null; + } + + return ( @@ -91,9 +97,11 @@ export const BottomDrawer = ({ interval, formatter, view }: Props) => { {isOpen ? hideHistory : showHistory} - - - + {nodeType === 'pod' && ( + + + + )} - + ); } From 75812a725d0c5b4636f8908b00e64d0f2aa2c9f6 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:56:20 +0200 Subject: [PATCH 34/68] [Cloud Security] Fix search bar UI for rule page (#151294) --- .../public/pages/rules/rules_table_header.tsx | 50 ++----------------- .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 4 files changed, 5 insertions(+), 51 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx index 5f6e3887647c8..2cebe21dac53d 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx @@ -5,8 +5,7 @@ * 2.0. */ import React, { useState } from 'react'; -import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiFieldSearch, EuiFlexItem } from '@elastic/eui'; import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; @@ -17,28 +16,8 @@ interface RulesTableToolbarProps { isSearching: boolean; } -interface CounterProps { - count: number; -} - -export const RulesTableHeader = ({ - search, - totalRulesCount, - searchValue, - isSearching, -}: RulesTableToolbarProps) => ( -

- - - - -
-); - -const Counters = ({ total }: { total: number }) => ( - - - +export const RulesTableHeader = ({ search, searchValue, isSearching }: RulesTableToolbarProps) => ( + ); const SEARCH_DEBOUNCE_MS = 300; @@ -53,11 +32,11 @@ const SearchField = ({ useDebounce(() => search(localValue), SEARCH_DEBOUNCE_MS, [localValue]); return ( - + setLocalValue(e.target.value)} @@ -67,22 +46,3 @@ const SearchField = ({ ); }; - -const TotalRulesCount = ({ count }: CounterProps) => ( - }} - /> -); - -const RulesCountBold = ({ count }: CounterProps) => ( - <> - {count} - - -); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 8182c753868a9..cbca7476239df 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -10024,8 +10024,6 @@ "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "Affichage de {pageStart}-{pageEnd} sur {total} {type}", "xpack.csp.findings.findingsTableCell.addFilterButton": "Ajouter un filtre {field}", "xpack.csp.findings.findingsTableCell.addNegateFilterButton": "Ajouter un filtre {field} négatif", - "xpack.csp.rules.header.rulesCountLabel": "{count, plural, one { règle} other { règles}}", - "xpack.csp.rules.header.totalRulesCount": "Affichage des {rules}", "xpack.csp.rules.rulePageHeader.pageHeaderTitle": "Règles - {integrationName}", "xpack.csp.subscriptionNotAllowed.promptDescription": "Pour utiliser ces fonctionnalités de sécurité du cloud, vous devez {link}.", "xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundTitle": "Aucune intégration Benchmark trouvée", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ece6fbb1d5c31..89c266124c0d7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10013,8 +10013,6 @@ "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "{total}件中{pageStart}-{pageEnd}件の{type}を表示しています", "xpack.csp.findings.findingsTableCell.addFilterButton": "{field}フィルターを追加", "xpack.csp.findings.findingsTableCell.addNegateFilterButton": "{field}否定フィルターを追加", - "xpack.csp.rules.header.rulesCountLabel": "{count, plural, other {個のルール}}", - "xpack.csp.rules.header.totalRulesCount": "{rules}を表示しています", "xpack.csp.rules.rulePageHeader.pageHeaderTitle": "ルール - {integrationName}", "xpack.csp.subscriptionNotAllowed.promptDescription": "これらのクラウドセキュリティ機能を使用するには、{link}する必要があります。", "xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundTitle": "ベンチマーク統合が見つかりません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a8acae9de7337..bc922fb478e65 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10028,8 +10028,6 @@ "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "正在显示第 {pageStart}-{pageEnd} 个(共 {total} 个){type}", "xpack.csp.findings.findingsTableCell.addFilterButton": "添加 {field} 筛选", "xpack.csp.findings.findingsTableCell.addNegateFilterButton": "添加 {field} 作废筛选", - "xpack.csp.rules.header.rulesCountLabel": "{count, plural, other { 规则}}", - "xpack.csp.rules.header.totalRulesCount": "正在显示 {rules}", "xpack.csp.rules.rulePageHeader.pageHeaderTitle": "规则 - {integrationName}", "xpack.csp.subscriptionNotAllowed.promptDescription": "要使用这些云安全功能,您必须 {link}。", "xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundTitle": "找不到基准集成", From 1ef33c488f24135dcedec87ecd5be9c2470ec1de Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:58:05 +0200 Subject: [PATCH 35/68] [Cloud Security] Center evaluation badge text (#151261) --- .../public/components/csp_evaluation_badge.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx b/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx index 24ca4cd4fe4eb..c1e1b52963805 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/csp_evaluation_badge.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiBadge, type EuiBadgeProps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; interface Props { type: 'passed' | 'failed'; @@ -15,7 +16,7 @@ interface Props { // 'fail' / 'pass' are same chars length, but not same width size. // 46px is used to make sure the badge is always the same width. -const BADGE_WIDTH = 46; +const BADGE_WIDTH = '46px'; const getColor = (type: Props['type']): EuiBadgeProps['color'] => { if (type === 'passed') return 'success'; @@ -26,7 +27,11 @@ const getColor = (type: Props['type']): EuiBadgeProps['color'] => { export const CspEvaluationBadge = ({ type }: Props) => ( {type === 'failed' ? ( From 6c828cf72df77cd8d5d8573b1d0e87fb6784610e Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Wed, 15 Feb 2023 11:03:59 -0600 Subject: [PATCH 36/68] Add `ReactQueryDevTools` to cases (#151180) I noticed the fleet plugin is also using React Query and has the dev tools enabled. This adds the same to cases. ![CleanShot 2023-02-14 at 11 01 37](https://user-images.githubusercontent.com/9912/218806318-5acb21d3-e24b-46e3-8b2e-2c845e658ab3.gif) --- .../all_cases/selector_modal/all_cases_selector_modal.tsx | 2 ++ x-pack/plugins/cases/public/components/app/routes.tsx | 2 ++ .../public/components/create/flyout/create_case_flyout.tsx | 2 ++ x-pack/plugins/cases/public/components/recent_cases/index.tsx | 2 ++ 4 files changed, 8 insertions(+) diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx index c04ea59dfebc5..6a9629699734b 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; import { QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import type { Case, CaseStatusWithAllStatus } from '../../../../common/ui/types'; import * as i18n from '../../../common/translations'; import { AllCasesList } from '../all_cases_list'; @@ -56,6 +57,7 @@ export const AllCasesSelectorModal = React.memo( return isModalOpen ? ( + {i18n.SELECT_CASE_TITLE} diff --git a/x-pack/plugins/cases/public/components/app/routes.tsx b/x-pack/plugins/cases/public/components/app/routes.tsx index 1d8b4249899e5..8f544a900b879 100644 --- a/x-pack/plugins/cases/public/components/app/routes.tsx +++ b/x-pack/plugins/cases/public/components/app/routes.tsx @@ -10,6 +10,7 @@ import { Redirect, Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; import { QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { EuiLoadingSpinner } from '@elastic/eui'; import { AllCases } from '../all_cases'; import { CreateCase } from '../create'; @@ -53,6 +54,7 @@ const CasesRoutesComponent: React.FC = ({ return ( + diff --git a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx index d20d14c574698..3cab988a5ab74 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx @@ -10,6 +10,7 @@ import styled, { createGlobalStyle } from 'styled-components'; import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui'; import { QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import type { CasePostRequest } from '../../../../common/api'; import * as i18n from '../translations'; import type { Case } from '../../../../common/ui/types'; @@ -80,6 +81,7 @@ export const CreateCaseFlyout = React.memo( return ( + { return ( + ); From ceb01e4100673a7f6eea600f983a369e0dadb522 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 15 Feb 2023 11:15:17 -0600 Subject: [PATCH 37/68] [build] Fix chromium install path (#151208) Fixes an issue introduced in 1b8581540295fde746dae6b4a09d74fb5821bfef Currently during builds chromium is installed to the `x-pack/reporting` directory. Under the new package management system plugins are installed in node_modules, and the reporting plugin is not able to find the path of chromium. This patches the path. Ideally we could install chromium before the plugin is installed as a package, but plugin installation is run once for all platforms, and later reporting is run for each platform. https://buildkite.com/elastic/kibana-artifacts-snapshot/builds/1222 --- src/dev/build/tasks/install_chromium.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dev/build/tasks/install_chromium.js b/src/dev/build/tasks/install_chromium.js index 542b36086d3ff..23e7ba6b5998c 100644 --- a/src/dev/build/tasks/install_chromium.js +++ b/src/dev/build/tasks/install_chromium.js @@ -39,7 +39,10 @@ export const InstallChromium = { log: log.write.bind(log), }; - const path = build.resolvePathForPlatform(platform, 'x-pack/plugins/screenshotting/chromium'); + const path = build.resolvePathForPlatform( + platform, + 'node_modules/@kbn/screenshotting-plugin/chromium' + ); await install(logger, pkg, path); } }, From 02eccd8081cec5dc036f389675ad470915637a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20=C3=81brah=C3=A1m?= Date: Wed, 15 Feb 2023 18:17:16 +0100 Subject: [PATCH 38/68] [Security Solution] Fix flaky policy list page integration test (#150204) ## Summary This PR updates the Policy List (used to be unit but now) integration tests with the modifications that have been implemented since the test was skipped, and tried to fix the flakiness. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../integration_tests/policy_list.test.tsx | 101 +++++++++--------- .../services/policies/test_mock_utils.ts | 22 ++-- 2 files changed, 62 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/integration_tests/policy_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/integration_tests/policy_list.test.tsx index 3709e2dad8378..369df34fdff4a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/integration_tests/policy_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/integration_tests/policy_list.test.tsx @@ -6,32 +6,36 @@ */ import React from 'react'; -import { act, waitFor, fireEvent } from '@testing-library/react'; +import { act, waitFor } from '@testing-library/react'; import type { AppContextTestRender } from '../../../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../../../common/mock/endpoint'; import { sendGetEndpointSpecificPackagePolicies } from '../../../../services/policies/policies'; import { sendGetEndpointSpecificPackagePoliciesMock } from '../../../../services/policies/test_mock_utils'; import { PolicyList } from '../policy_list'; -import { sendBulkGetAgentPolicyList } from '../../../../services/policies/ingest'; import type { GetPolicyListResponse } from '../../types'; import { getEndpointListPath, getPoliciesPath } from '../../../../common/routing'; import { APP_UI_ID } from '../../../../../../common/constants'; +import { useUserPrivileges } from '../../../../../common/components/user_privileges'; +import userEvent from '@testing-library/user-event'; +import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; jest.mock('../../../../services/policies/policies'); -jest.mock('../../../../services/policies/ingest'); +jest.mock('../../../../../common/components/user_privileges'); const getPackagePolicies = sendGetEndpointSpecificPackagePolicies as jest.Mock; +const useUserPrivilegesMock = useUserPrivileges as jest.Mock; -const mockedSendBulkGetAgentPolicies = sendBulkGetAgentPolicyList as jest.Mock; - -// FLAKY: https://github.com/elastic/kibana/issues/143436 -describe.skip('When on the policy list page', () => { +describe('When on the policy list page', () => { let render: () => ReturnType; let renderResult: ReturnType; let history: AppContextTestRender['history']; let mockedContext: AppContextTestRender; beforeEach(() => { + useUserPrivilegesMock.mockReturnValue({ + endpointPrivileges: { canReadEndpointList: true, canAccessFleet: true, loading: false }, + }); + mockedContext = createAppRootMockRenderer(); ({ history } = mockedContext); render = () => (renderResult = mockedContext.render()); @@ -58,39 +62,36 @@ describe.skip('When on the policy list page', () => { afterEach(() => { getPackagePolicies.mockReset(); }); - it('should show the empty page', () => { - expect(renderResult.getByTestId('emptyPolicyTable')).toBeTruthy(); + it('should show the empty page', async () => { + await waitFor(() => { + expect(renderResult.getByTestId('emptyPolicyTable')).toBeTruthy(); + }); }); it('should show instruction text and a button to add the Endpoint Security integration', async () => { - expect( - renderResult.findByText( - 'From this page, you’ll be able to view and manage the Elastic Defend Integration policies in your environment running Elastic Defend.' - ) - ).toBeTruthy(); await waitFor(() => { + expect( + renderResult.getByText( + 'From this page, you’ll be able to view and manage the Elastic Defend Integration policies in your environment running Elastic Defend.' + ) + ).toBeTruthy(); + expect(renderResult.getByTestId('onboardingStartButton')).toBeTruthy(); }); }); }); describe('and data exists', () => { - const policies: GetPolicyListResponse = sendGetEndpointSpecificPackagePoliciesMock(); + const policies: GetPolicyListResponse = sendGetEndpointSpecificPackagePoliciesMock({ + agentsPerPolicy: 4, + }); beforeEach(async () => { getPackagePolicies.mockReturnValue(policies); - mockedSendBulkGetAgentPolicies.mockReturnValue({ - items: [ - { package_policies: [{ id: policies.items[0].id }], agents: 4 }, - { package_policies: [{ id: policies.items[1].id }], agents: 2 }, - { package_policies: [{ id: policies.items[2].id }], agents: 5 }, - { package_policies: [{ id: policies.items[3].id }], agents: 1 }, - { package_policies: [{ id: policies.items[4].id }], agents: 3 }, - ], - }); render(); await waitFor(() => { expect(sendGetEndpointSpecificPackagePolicies).toHaveBeenCalled(); - expect(sendBulkGetAgentPolicyList).toHaveBeenCalled(); + expect(getPackagePolicies).toHaveBeenCalled(); + expect(renderResult.getByTestId('policyListTable')).toBeTruthy(); }); }); it('should display the policy list table', () => { @@ -120,7 +121,7 @@ describe.skip('When on the policy list page', () => { expect(firstUpdatedByName.textContent).toEqual(expectedAvatarName); }); - it('should show the correct endpoint count', async () => { + it('should show the correct endpoint count', () => { const endpointCount = renderResult.getAllByTestId('policyEndpointCountLink'); expect(endpointCount[0].textContent).toBe('4'); }); @@ -141,15 +142,16 @@ describe.skip('When on the policy list page', () => { }, }; const endpointCount = renderResult.getAllByTestId('policyEndpointCountLink')[0]; - fireEvent.click(endpointCount); + userEvent.click(endpointCount); expect(history.location.pathname).toEqual(getEndpointListPath({ name: 'endpointList' })); expect(history.location.search).toEqual(filterByPolicyQuery); expect(history.location.state).toEqual(backLink); // reset test to the policy page - history.push('/administration/policies'); - render(); + act(() => { + history.push('/administration/policies'); + }); }); }); describe('pagination', () => { @@ -166,7 +168,6 @@ describe.skip('When on the policy list page', () => { await waitFor(() => { expect(getPackagePolicies).toHaveBeenCalled(); expect(sendGetEndpointSpecificPackagePolicies).toHaveBeenCalled(); - expect(mockedSendBulkGetAgentPolicies).toHaveBeenCalled(); }); }); afterEach(() => { @@ -176,15 +177,14 @@ describe.skip('When on the policy list page', () => { await waitFor(() => { expect(renderResult.getByTestId('pagination-button-next')).toBeTruthy(); }); - act(() => { - renderResult.getByTestId('pagination-button-next').click(); - }); + userEvent.click(renderResult.getByTestId('pagination-button-next')); await waitFor(() => { expect(getPackagePolicies).toHaveBeenCalledTimes(2); }); expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ page: 2, perPage: 10, + withAgentCount: true, }); }); @@ -192,13 +192,9 @@ describe.skip('When on the policy list page', () => { await waitFor(() => { expect(renderResult.getByTestId('tablePaginationPopoverButton')).toBeTruthy(); }); - act(() => { - renderResult.getByTestId('tablePaginationPopoverButton').click(); - }); - const pageSize20 = await renderResult.findByTestId('tablePagination-20-rows'); - act(() => { - pageSize20.click(); - }); + userEvent.click(renderResult.getByTestId('tablePaginationPopoverButton')); + await waitForEuiPopoverOpen(); + userEvent.click(renderResult.getByTestId('tablePagination-20-rows')); await waitFor(() => { expect(getPackagePolicies).toHaveBeenCalledTimes(2); @@ -206,6 +202,7 @@ describe.skip('When on the policy list page', () => { expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ page: 1, perPage: 20, + withAgentCount: true, }); }); @@ -218,6 +215,7 @@ describe.skip('When on the policy list page', () => { expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ page: 3, perPage: 50, + withAgentCount: true, }); }); }); @@ -231,19 +229,15 @@ describe.skip('When on the policy list page', () => { }); // change pageSize - await act(async () => { - (await renderResult.getByTestId('tablePaginationPopoverButton')).click(); - }); - const pageSize10 = await renderResult.findByTestId('tablePagination-10-rows'); - act(() => { - pageSize10.click(); - }); + userEvent.click(renderResult.getByTestId('tablePaginationPopoverButton')); + await waitForEuiPopoverOpen(); + userEvent.click(renderResult.getByTestId('tablePagination-10-rows')); - expect(sendGetEndpointSpecificPackagePolicies).toHaveBeenLastCalledWith(expect.any(Object), { - query: { - page: 1, - perPage: 10, - }, + await waitFor(() => { + expect(sendGetEndpointSpecificPackagePolicies).toHaveBeenLastCalledWith( + expect.any(Object), + { query: { page: 1, perPage: 10, withAgentCount: true } } + ); }); }); it('should set page to 1 if user tries to force an invalid page number', async () => { @@ -257,6 +251,7 @@ describe.skip('When on the policy list page', () => { expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ page: 1, perPage: 20, + withAgentCount: true, }); }); it('should set page size to 10 (management default) if page size is set to anything other than 10, 20, or 50', async () => { @@ -270,6 +265,7 @@ describe.skip('When on the policy list page', () => { expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ page: 2, perPage: 10, + withAgentCount: true, }); }); it('should set page to last defined page number value if multiple values exist for page in the URL, i.e. page=2&page=4&page=3 then page is set to 3', async () => { @@ -283,6 +279,7 @@ describe.skip('When on the policy list page', () => { expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ page: 3, perPage: 10, + withAgentCount: true, }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts b/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts index af7a531d5a352..b2a34318bf704 100644 --- a/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts +++ b/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts @@ -7,17 +7,21 @@ import { FleetPackagePolicyGenerator } from '../../../../common/endpoint/data_generators/fleet_package_policy_generator'; import type { GetPolicyListResponse } from '../../pages/policy/types'; -export const sendGetEndpointSpecificPackagePoliciesMock = ( - params: { - page: number; - perPage: number; - count: number; - } = { page: 1, perPage: 20, count: 5 } -): GetPolicyListResponse => { - const { page, perPage, count } = params; +export const sendGetEndpointSpecificPackagePoliciesMock = ({ + page = 1, + perPage = 20, + count = 5, + agentsPerPolicy, +}: { + page?: number; + perPage?: number; + count?: number; + agentsPerPolicy?: number; +} = {}): GetPolicyListResponse => { const generator = new FleetPackagePolicyGenerator(); const items = Array.from({ length: count }, (_, index) => { - const policy = generator.generateEndpointPackagePolicy(); + const overrides = agentsPerPolicy !== undefined ? { agents: agentsPerPolicy } : undefined; + const policy = generator.generateEndpointPackagePolicy(overrides); policy.name += ` ${index}`; return policy; }); From 79b0f04d77b80dafc1de24748ad68a7d2acad3c8 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 15 Feb 2023 19:19:18 +0200 Subject: [PATCH 39/68] [Unified search] Add locale to the date picker (#151258) ## Summary Part of https://github.com/elastic/kibana/issues/151051 Passes the locale to the Date Picker Component image --- .../public/query_string_input/query_bar_top_row.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index d4d35f2545b73..8d1e733cc0593 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -405,6 +405,7 @@ export const QueryBarTopRow = React.memo( onRefreshChange={props.onRefreshChange} showUpdateButton={false} recentlyUsedRanges={recentlyUsedRanges} + locale={i18n.getLocale()} commonlyUsedRanges={commonlyUsedRanges} dateFormat={uiSettings.get('dateFormat')} isAutoRefreshOnly={showAutoRefreshOnly} From 1535378b45667fc986fa676ce641ff0fa1e54dc6 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 15 Feb 2023 19:49:35 +0200 Subject: [PATCH 40/68] [Cloud Security] Close rule flyout on outside click (#151313) --- .../cloud_security_posture/public/pages/rules/rules_flyout.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx index eb6f2aeb526f7..ca7afa76984dc 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_flyout.tsx @@ -55,6 +55,7 @@ export const RuleFlyout = ({ onClose, rule }: RuleFlyoutProps) => { ownFocus={false} onClose={onClose} data-test-subj={TEST_SUBJECTS.CSP_RULES_FLYOUT_CONTAINER} + outsideClickCloses > From 12ae1a966be9bb18a2fc0d17afe18883c77c9c55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:56:50 -0600 Subject: [PATCH 41/68] Update dependency chromedriver to v110 (main) (#151329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [chromedriver](https://togithub.com/giggio/node-chromedriver) | [`^109.0.0` -> `^110.0.0`](https://renovatebot.com/diffs/npm/chromedriver/109.0.0/110.0.0) | [![age](https://badges.renovateapi.com/packages/npm/chromedriver/110.0.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/chromedriver/110.0.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/chromedriver/110.0.0/compatibility-slim/109.0.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/chromedriver/110.0.0/confidence-slim/109.0.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
giggio/node-chromedriver ### [`v110.0.0`](https://togithub.com/giggio/node-chromedriver/compare/109.0.0...110.0.0) [Compare Source](https://togithub.com/giggio/node-chromedriver/compare/109.0.0...110.0.0)
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/elastic/kibana). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 996b65f226ccb..61441db1fd8ef 100644 --- a/package.json +++ b/package.json @@ -1309,7 +1309,7 @@ "backport": "^8.9.7", "callsites": "^3.1.0", "chance": "1.0.18", - "chromedriver": "^109.0.0", + "chromedriver": "^110.0.0", "clean-webpack-plugin": "^3.0.0", "compression-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^6.0.2", diff --git a/yarn.lock b/yarn.lock index 188c3c05eec83..164ecb0e54d82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11797,10 +11797,10 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@^109.0.0: - version "109.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-109.0.0.tgz#9407af72f2a01bb19e6db1b85cfc87eb3acd4f6b" - integrity sha512-jdmBq11IUwfThLFiygGTZ89qbROSQI4bICQjvOVQy2Bqr1LwC+MFkhwyZp3YG99eehQbZuTlQmmfCZBfpewTNA== +chromedriver@^110.0.0: + version "110.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-110.0.0.tgz#d00a1a2976592d933faa8e9839e97692922834a4" + integrity sha512-Le6q8xrA/3fAt+g8qiN0YjsYxINIhQMC6wj9X3W5L77uN4NspEzklDrqYNwBcEVn7PcAEJ73nLlS7mTyZRspHA== dependencies: "@testim/chrome-version" "^1.1.3" axios "^1.2.1" From 168b9fd395bc9e2729e8b98046cfcffdcfb69aff Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Wed, 15 Feb 2023 10:00:47 -0800 Subject: [PATCH 42/68] [DOCS] Fix automated screenshot for ML rule notification (#151239) Co-authored-by: Robert Oskamp --- .../anomaly_detection/generate_anomaly_alerts.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts index 5246eee8c5ab3..57bae974ce273 100644 --- a/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts +++ b/x-pack/test/screenshot_creation/apps/ml_docs/anomaly_detection/generate_anomaly_alerts.ts @@ -69,6 +69,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const commonScreenshots = getService('commonScreenshots'); const browser = getService('browser'); const actions = getService('actions'); + const testSubjects = getService('testSubjects'); const screenshotDirectories = ['ml_docs', 'anomaly_detection']; @@ -105,13 +106,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await ml.navigation.navigateToAlertsAndAction(); await pageObjects.triggersActionsUI.clickCreateAlertButton(); await ml.alerting.setRuleName('test-ecommerce'); - - await ml.alerting.openNotifySelection(); + const searchBox = await testSubjects.find('ruleSearchField'); + await searchBox.click(); + await searchBox.clearValue(); + await searchBox.type('ml'); + await searchBox.pressKeys(browser.keys.ENTER); + await ml.testExecution.logTestStep('take screenshot'); await commonScreenshots.takeScreenshot('ml-rule', screenshotDirectories, 1920, 1400); - // close popover - await browser.pressKeys(browser.keys.ESCAPE); - await ml.alerting.selectAnomalyDetectionJobHealthAlertType(); await ml.alerting.selectJobs([testJobId]); await ml.testExecution.logTestStep('take screenshot'); From e5895c30ba3eb21eb9121bf2007b0e9f55616c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Wed, 15 Feb 2023 19:02:46 +0100 Subject: [PATCH 43/68] [Defend Workflows] PoC of e2e endpoint testing (#148893) --- .buildkite/ftr_configs.yml | 1 + .../index_fleet_endpoint_policy.ts | 6 ++- x-pack/plugins/security_solution/package.json | 2 + .../public/management/cypress.config.ts | 2 +- .../cypress/e2e/endpoint/endpoints.cy.ts | 19 ++++++++ .../artifact_tabs_in_policy_details.cy.ts | 12 ++--- .../e2e/{ => mocked_data}/artifacts.cy.ts | 10 ++--- .../e2e/{ => mocked_data}/endpoints.cy.ts | 4 +- .../management/cypress_endpoint.config.ts | 41 +++++++++++++++++ .../scripts/endpoint/common/fleet_services.ts | 2 +- .../endpoint/common/localhost_services.ts | 24 ++++++---- .../endpoint_agent_runner/elastic_endpoint.ts | 7 ++- .../endpoint_agent_runner/fleet_server.ts | 18 +++++--- .../endpoint/endpoint_agent_runner/runtime.ts | 5 ++- .../endpoint/endpoint_agent_runner/types.ts | 4 +- x-pack/test/defend_workflows_cypress/agent.ts | 35 +++++++++++++++ .../test/defend_workflows_cypress/config.ts | 7 +++ .../endpoint_config.ts | 32 ++++++++++++++ .../defend_workflows_cypress/fleet_server.ts | 37 ++++++++++++++++ .../resource_manager.ts | 15 +++++++ .../test/defend_workflows_cypress/runner.ts | 44 ++++++++++++++++++- x-pack/test/defend_workflows_cypress/utils.ts | 41 +++++++++++++++++ 22 files changed, 330 insertions(+), 38 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.ts rename x-pack/plugins/security_solution/public/management/cypress/e2e/{ => mocked_data}/artifact_tabs_in_policy_details.cy.ts (95%) rename x-pack/plugins/security_solution/public/management/cypress/e2e/{ => mocked_data}/artifacts.cy.ts (93%) rename x-pack/plugins/security_solution/public/management/cypress/e2e/{ => mocked_data}/endpoints.cy.ts (82%) create mode 100644 x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts create mode 100644 x-pack/test/defend_workflows_cypress/agent.ts create mode 100644 x-pack/test/defend_workflows_cypress/endpoint_config.ts create mode 100644 x-pack/test/defend_workflows_cypress/fleet_server.ts create mode 100644 x-pack/test/defend_workflows_cypress/resource_manager.ts create mode 100644 x-pack/test/defend_workflows_cypress/utils.ts diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 2bfcce064d5ce..62a42c4d4335e 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -26,6 +26,7 @@ disabled: - x-pack/test/functional_enterprise_search/cypress.config.ts - x-pack/test/defend_workflows_cypress/cli_config.ts - x-pack/test/defend_workflows_cypress/config.ts + - x-pack/test/defend_workflows_cypress/endpoint_config.ts - x-pack/test/defend_workflows_cypress/visual_config.ts - x-pack/test/osquery_cypress/cli_config.ts - x-pack/test/osquery_cypress/config.ts diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts index 1c2326832402e..312809e2f48e4 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts @@ -16,6 +16,7 @@ import type { DeleteAgentPolicyResponse, PostDeletePackagePoliciesResponse, } from '@kbn/fleet-plugin/common'; +import { kibanaPackageJson } from '@kbn/repo-info'; import { AGENT_POLICY_API_ROUTES, PACKAGE_POLICY_API_ROUTES } from '@kbn/fleet-plugin/common'; import type { PolicyData } from '../types'; import { policyFactory as policyConfigFactory } from '../models/policy_config'; @@ -33,7 +34,7 @@ export interface IndexedFleetEndpointPolicyResponse { export const indexFleetEndpointPolicy = async ( kbnClient: KbnClient, policyName: string, - endpointPackageVersion: string = '8.0.0', + endpointPackageVersion: string = kibanaPackageJson.version, agentPolicyName?: string ): Promise => { const response: IndexedFleetEndpointPolicyResponse = { @@ -47,6 +48,7 @@ export const indexFleetEndpointPolicy = async ( agentPolicyName || `Policy for ${policyName} (${Math.random().toString(36).substr(2, 5)})`, description: `Policy created with endpoint data generator (${policyName})`, namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], }; let agentPolicy: AxiosResponse; @@ -86,7 +88,7 @@ export const indexFleetEndpointPolicy = async ( namespace: 'default', package: { name: 'endpoint', - title: 'endpoint', + title: 'Elastic Defend', version: endpointPackageVersion, }, }; diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index b886836ec9b18..6bee06261023a 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -26,6 +26,8 @@ "cypress:dw:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/defend_workflows_cypress/visual_config.ts", "cypress:dw:run": "yarn cypress run --config-file ./public/management/cypress.config.ts", "cypress:dw:run-as-ci": "node ../../../scripts/functional_tests --config ../../test/defend_workflows_cypress/cli_config.ts", + "cypress:dw:endpoint:open": "yarn cypress open --config-file ./public/management/cypress_endpoint.config.ts", + "cypress:dw:endpoint:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/defend_workflows_cypress/endpoint_config.ts", "junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results && mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/", "test:generate": "node scripts/endpoint/resolver_generator" } diff --git a/x-pack/plugins/security_solution/public/management/cypress.config.ts b/x-pack/plugins/security_solution/public/management/cypress.config.ts index 6ff919ea7effe..feaa2c8459e46 100644 --- a/x-pack/plugins/security_solution/public/management/cypress.config.ts +++ b/x-pack/plugins/security_solution/public/management/cypress.config.ts @@ -34,7 +34,7 @@ export default defineCypressConfig({ e2e: { baseUrl: 'http://localhost:5620', supportFile: 'public/management/cypress/support/e2e.ts', - specPattern: 'public/management/cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', + specPattern: 'public/management/cypress/e2e/mocked_data/*.cy.{js,jsx,ts,tsx}', experimentalRunAllSpecs: true, }, }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.ts new file mode 100644 index 0000000000000..3eec34c967c21 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint/endpoints.cy.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 { login } from '../../tasks/login'; + +describe('Endpoints page', () => { + beforeEach(() => { + login(); + }); + + it('Loads the endpoints page', () => { + cy.visit('/app/security/administration/endpoints'); + cy.contains('Hosts running Elastic Defend').should('exist'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifact_tabs_in_policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts similarity index 95% rename from x-pack/plugins/security_solution/public/management/cypress/e2e/artifact_tabs_in_policy_details.cy.ts rename to x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts index a74eba6274034..02c11cd441e3f 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifact_tabs_in_policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifact_tabs_in_policy_details.cy.ts @@ -5,18 +5,18 @@ * 2.0. */ -import { getEndpointSecurityPolicyManager } from '../../../../scripts/endpoint/common/roles_users/endpoint_security_policy_manager'; -import { getArtifactsListTestsData } from '../fixtures/artifacts_page'; +import { getEndpointSecurityPolicyManager } from '../../../../../scripts/endpoint/common/roles_users/endpoint_security_policy_manager'; +import { getArtifactsListTestsData } from '../../fixtures/artifacts_page'; import { createPerPolicyArtifact, createArtifactList, removeAllArtifacts, removeExceptionsList, yieldFirstPolicyID, -} from '../tasks/artifacts'; -import { loadEndpointDataForEventFiltersIfNeeded } from '../tasks/load_endpoint_data'; -import { login, loginWithCustomRole, loginWithRole, ROLE } from '../tasks/login'; -import { performUserActions } from '../tasks/perform_user_actions'; +} from '../../tasks/artifacts'; +import { loadEndpointDataForEventFiltersIfNeeded } from '../../tasks/load_endpoint_data'; +import { login, loginWithCustomRole, loginWithRole, ROLE } from '../../tasks/login'; +import { performUserActions } from '../../tasks/perform_user_actions'; const loginWithPrivilegeAll = () => { loginWithRole(ROLE.endpoint_security_policy_manager); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifacts.cy.ts similarity index 93% rename from x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts.cy.ts rename to x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifacts.cy.ts index 166d506514553..6c8c345674d20 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/artifacts.cy.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { login, loginWithRole, ROLE } from '../tasks/login'; +import { login, loginWithRole, ROLE } from '../../tasks/login'; -import { getArtifactsListTestsData } from '../fixtures/artifacts_page'; -import { removeAllArtifacts } from '../tasks/artifacts'; -import { performUserActions } from '../tasks/perform_user_actions'; -import { loadEndpointDataForEventFiltersIfNeeded } from '../tasks/load_endpoint_data'; +import { getArtifactsListTestsData } from '../../fixtures/artifacts_page'; +import { removeAllArtifacts } from '../../tasks/artifacts'; +import { performUserActions } from '../../tasks/perform_user_actions'; +import { loadEndpointDataForEventFiltersIfNeeded } from '../../tasks/load_endpoint_data'; const loginWithWriteAccess = (url: string) => { loginWithRole(ROLE.analyst_hunter); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoints.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoints.cy.ts similarity index 82% rename from x-pack/plugins/security_solution/public/management/cypress/e2e/endpoints.cy.ts rename to x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoints.cy.ts index 508b606f24263..35b931675a6c2 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoints.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/endpoints.cy.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { login } from '../tasks/login'; -import { runEndpointLoaderScript } from '../tasks/run_endpoint_loader'; +import { login } from '../../tasks/login'; +import { runEndpointLoaderScript } from '../../tasks/run_endpoint_loader'; describe('Endpoints page', () => { before(() => { diff --git a/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts b/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts new file mode 100644 index 0000000000000..d1487b6d990d6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress_endpoint.config.ts @@ -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 { defineCypressConfig } from '@kbn/cypress-config'; + +// eslint-disable-next-line import/no-default-export +export default defineCypressConfig({ + defaultCommandTimeout: 60000, + execTimeout: 120000, + pageLoadTimeout: 12000, + + retries: { + runMode: 1, + openMode: 0, + }, + + screenshotsFolder: + '../../../target/kibana-security-solution/public/management/cypress/screenshots', + trashAssetsBeforeRuns: false, + video: false, + viewportHeight: 900, + viewportWidth: 1440, + experimentalStudio: true, + + env: { + 'cypress-react-selector': { + root: '#security-solution-app', + }, + }, + + e2e: { + baseUrl: 'http://localhost:5620', + supportFile: 'public/management/cypress/support/e2e.ts', + specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}', + experimentalRunAllSpecs: true, + }, +}); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts index effa09db0c388..1f4d28cecc569 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { pick } from 'lodash'; import type { Client, estypes } from '@elastic/elasticsearch'; import type { Agent, @@ -14,7 +15,6 @@ import type { GetAgentsResponse, } from '@kbn/fleet-plugin/common'; import { AGENT_API_ROUTES, agentPolicyRouteService, AGENTS_INDEX } from '@kbn/fleet-plugin/common'; -import { pick } from 'lodash'; import { ToolingLog } from '@kbn/tooling-log'; import type { KbnClient } from '@kbn/test'; import type { GetFleetServerHostsResponse } from '@kbn/fleet-plugin/common/types/rest_spec/fleet_server_hosts'; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts index 3b985f76dcd81..6a771d498f347 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts @@ -5,7 +5,7 @@ * 2.0. */ -import execa from 'execa'; +import { networkInterfaces } from 'node:os'; const POSSIBLE_LOCALHOST_VALUES: readonly string[] = [ 'localhost', @@ -15,13 +15,21 @@ const POSSIBLE_LOCALHOST_VALUES: readonly string[] = [ '0000:0000:0000:0000:0000:0000:0000:0000', ]; -export const getLocalhostRealIp = async (): Promise => { - // TODO:PT find better way to get host machine public IP. Command below is not x-platform - - return execa.commandSync( - "ipconfig getifaddr `scutil --dns |awk -F'[()]' '$1~/if_index/ {print $2;exit;}'`", - { shell: true } - ).stdout; +export const getLocalhostRealIp = (): string => { + for (const netInterfaceList of Object.values(networkInterfaces())) { + if (netInterfaceList) { + const netInterface = netInterfaceList.find( + (networkInterface) => + networkInterface.family === 'IPv4' && + networkInterface.internal === false && + networkInterface.address + ); + if (netInterface) { + return netInterface.address; + } + } + } + return '0.0.0.0'; }; export const isLocalhost = (hostname: string): boolean => { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts index e0938707ea5a0..dd123622d9c49 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts @@ -44,7 +44,8 @@ interface ElasticArtifactSearchResponse { }; } -export const enrollEndpointHost = async () => { +export const enrollEndpointHost = async (): Promise => { + let vmName; const { log, kbnClient, @@ -80,7 +81,7 @@ export const enrollEndpointHost = async () => { throw new Error(`No API enrollment key found for policy id [${policyId}]`); } - const vmName = `${username}-dev-${uniqueId}`; + vmName = `${username}-dev-${uniqueId}`; log.info(`Creating VM named: ${vmName}`); @@ -166,6 +167,8 @@ export const enrollEndpointHost = async () => { } log.indent(-4); + + return vmName; }; const getAgentDownloadUrl = async (version: string): Promise => { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts index e8891f04aa6e9..5ca183e550c6f 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts @@ -44,7 +44,8 @@ import { } from '../common/fleet_services'; import { getRuntimeServices } from './runtime'; -export const runFleetServerIfNeeded = async () => { +export const runFleetServerIfNeeded = async (): Promise => { + let fleetServerContainerId; const { log, kibana: { isLocalhost: isKibanaOnLocalhost }, @@ -69,7 +70,7 @@ export const runFleetServerIfNeeded = async () => { await configureFleetIfNeeded(); } - await startFleetServerWithDocker({ + fleetServerContainerId = await startFleetServerWithDocker({ policyId: fleetServerAgentPolicyId, serviceToken, }); @@ -80,6 +81,8 @@ export const runFleetServerIfNeeded = async () => { } log.indent(-4); + + return fleetServerContainerId; }; const isFleetServerEnrolled = async () => { @@ -178,13 +181,14 @@ const generateFleetServiceToken = async (): Promise => { return serviceToken; }; -const startFleetServerWithDocker = async ({ +export const startFleetServerWithDocker = async ({ policyId, serviceToken, }: { policyId: string; serviceToken: string; }) => { + let containerId; const { log, localhostRealIp, @@ -256,16 +260,16 @@ const startFleetServerWithDocker = async ({ (This is ok if one was not running already)`); }); + await addFleetServerHostToFleetSettings(`https://${localhostRealIp}:8220`); + log.verbose(`docker arguments:\n${dockerArgs.join(' ')}`); - const containerId = (await execa('docker', dockerArgs)).stdout; + containerId = (await execa('docker', dockerArgs)).stdout; const fleetServerAgent = await waitForHostToEnroll(kbnClient, containerName); log.verbose(`Fleet server enrolled agent:\n${JSON.stringify(fleetServerAgent, null, 2)}`); - await addFleetServerHostToFleetSettings(`https://${localhostRealIp}:8220`); - log.info(`Done. Fleet Server is running and connected to Fleet. Container Name: ${containerName} Container Id: ${containerId} @@ -280,6 +284,8 @@ const startFleetServerWithDocker = async ({ } log.indent(-4); + + return containerId; }; const configureFleetIfNeeded = async () => { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts index 989b99690c4ec..24007b7ba3479 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts @@ -12,8 +12,9 @@ import type { RuntimeServices } from '../common/stack_services'; import { createRuntimeServices } from '../common/stack_services'; interface EndpointRunnerRuntimeServices extends RuntimeServices { - options: Required< - Omit + options: Omit< + StartRuntimeServicesOptions, + 'kibanaUrl' | 'elasticUrl' | 'username' | 'password' | 'log' >; } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts index 2af229bc74a57..09adc50b098af 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts @@ -12,7 +12,7 @@ export interface StartRuntimeServicesOptions { elasticUrl: string; username: string; password: string; - version: string; - policy: string; + version?: string; + policy?: string; log?: ToolingLog; } diff --git a/x-pack/test/defend_workflows_cypress/agent.ts b/x-pack/test/defend_workflows_cypress/agent.ts new file mode 100644 index 0000000000000..c34eb728432c9 --- /dev/null +++ b/x-pack/test/defend_workflows_cypress/agent.ts @@ -0,0 +1,35 @@ +/* + * 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 execa from 'execa'; +import { ToolingLog } from '@kbn/tooling-log'; +import { enrollEndpointHost } from '@kbn/security-solution-plugin/scripts/endpoint/endpoint_agent_runner/elastic_endpoint'; +import { Manager } from './resource_manager'; + +export class AgentManager extends Manager { + private log: ToolingLog; + private vmName?: string; + constructor(log: ToolingLog) { + super(); + this.log = log; + this.vmName = undefined; + } + + public async setup() { + this.vmName = await enrollEndpointHost(); + } + + public cleanup() { + super.cleanup(); + this.log.info('Cleaning up the agent process'); + if (this.vmName) { + execa.commandSync(`multipass delete -p ${this.vmName}`); + + this.log.info('Agent process closed'); + } + } +} diff --git a/x-pack/test/defend_workflows_cypress/config.ts b/x-pack/test/defend_workflows_cypress/config.ts index 37d25c47825c2..a86916951c1fd 100644 --- a/x-pack/test/defend_workflows_cypress/config.ts +++ b/x-pack/test/defend_workflows_cypress/config.ts @@ -7,6 +7,7 @@ import { FtrConfigProviderContext } from '@kbn/test'; import { CA_CERT_PATH } from '@kbn/dev-utils'; +import { getLocalhostRealIp } from '@kbn/security-solution-plugin/scripts/endpoint/common/localhost_services'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { @@ -17,6 +18,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('../functional/config.base.js') ); + const hostIp = getLocalhostRealIp(); + return { ...kibanaCommonTestsConfig.getAll(), @@ -41,6 +44,10 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--csp.strict=false', // define custom kibana server args here `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, + `--xpack.fleet.agents.fleet_server.hosts=["https://${hostIp}:8220"]`, + `--xpack.fleet.agents.elasticsearch.host=http://${hostIp}:${kibanaCommonTestsConfig.get( + 'servers.elasticsearch.port' + )}`, // always install Endpoint package by default when Fleet sets up `--xpack.fleet.packages.0.name=endpoint`, `--xpack.fleet.packages.0.version=latest`, diff --git a/x-pack/test/defend_workflows_cypress/endpoint_config.ts b/x-pack/test/defend_workflows_cypress/endpoint_config.ts new file mode 100644 index 0000000000000..6dacf56791539 --- /dev/null +++ b/x-pack/test/defend_workflows_cypress/endpoint_config.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 { getLocalhostRealIp } from '@kbn/security-solution-plugin/scripts/endpoint/common/localhost_services'; +import { FtrConfigProviderContext } from '@kbn/test'; + +import { DefendWorkflowsCypressEndpointTestRunner } from './runner'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const defendWorkflowsCypressConfig = await readConfigFile(require.resolve('./config.ts')); + const config = defendWorkflowsCypressConfig.getAll(); + const hostIp = getLocalhostRealIp(); + + return { + ...config, + kbnTestServer: { + ...config.kbnTestServer, + serverArgs: [ + ...config.kbnTestServer.serverArgs, + `--xpack.fleet.agents.fleet_server.hosts=["https://${hostIp}:8220"]`, + `--xpack.fleet.agents.elasticsearch.host=http://${hostIp}:${defendWorkflowsCypressConfig.get( + 'servers.elasticsearch.port' + )}`, + ], + }, + testRunner: DefendWorkflowsCypressEndpointTestRunner, + }; +} diff --git a/x-pack/test/defend_workflows_cypress/fleet_server.ts b/x-pack/test/defend_workflows_cypress/fleet_server.ts new file mode 100644 index 0000000000000..c24cd9bb17e4c --- /dev/null +++ b/x-pack/test/defend_workflows_cypress/fleet_server.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. + */ + +import execa from 'execa'; +import { ToolingLog } from '@kbn/tooling-log'; +import { runFleetServerIfNeeded } from '@kbn/security-solution-plugin/scripts/endpoint/endpoint_agent_runner/fleet_server'; +import { Manager } from './resource_manager'; + +export class FleetManager extends Manager { + private fleetContainerId?: string; + private log: ToolingLog; + + constructor(log: ToolingLog) { + super(); + this.log = log; + } + + public async setup(): Promise { + this.fleetContainerId = await runFleetServerIfNeeded(); + } + + public cleanup() { + super.cleanup(); + + this.log.info('Removing old fleet config'); + if (this.fleetContainerId) { + this.log.info('Closing fleet process'); + + execa.sync('docker', ['kill', this.fleetContainerId]); + this.log.info('Fleet server process closed'); + } + } +} diff --git a/x-pack/test/defend_workflows_cypress/resource_manager.ts b/x-pack/test/defend_workflows_cypress/resource_manager.ts new file mode 100644 index 0000000000000..0c030ebfa9f56 --- /dev/null +++ b/x-pack/test/defend_workflows_cypress/resource_manager.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +const CLEANUP_EVENTS = ['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection']; +export class Manager { + constructor() { + const cleanup = () => this.cleanup(); + CLEANUP_EVENTS.forEach((ev) => process.on(ev, cleanup)); + } + cleanup() {} +} diff --git a/x-pack/test/defend_workflows_cypress/runner.ts b/x-pack/test/defend_workflows_cypress/runner.ts index 2053a6b67b3bc..fddf7867ba28c 100644 --- a/x-pack/test/defend_workflows_cypress/runner.ts +++ b/x-pack/test/defend_workflows_cypress/runner.ts @@ -8,8 +8,46 @@ import { resolve } from 'path'; import Url from 'url'; import { withProcRunner } from '@kbn/dev-proc-runner'; - +import { startRuntimeServices } from '@kbn/security-solution-plugin/scripts/endpoint/endpoint_agent_runner/runtime'; import { FtrProviderContext } from './ftr_provider_context'; +import { AgentManager } from './agent'; +import { FleetManager } from './fleet_server'; +import { getLatestAvailableAgentVersion } from './utils'; + +async function withFleetAgent( + { getService }: FtrProviderContext, + runner: (runnerEnv: Record) => Promise +) { + const log = getService('log'); + const config = getService('config'); + const kbnClient = getService('kibanaServer'); + + const elasticUrl = Url.format(config.get('servers.elasticsearch')); + const kibanaUrl = Url.format(config.get('servers.kibana')); + const username = config.get('servers.elasticsearch.username'); + const password = config.get('servers.elasticsearch.password'); + + await startRuntimeServices({ + log, + elasticUrl, + kibanaUrl, + username, + password, + version: await getLatestAvailableAgentVersion(kbnClient), + }); + + const fleetManager = new FleetManager(log); + const agentManager = new AgentManager(log); + + await fleetManager.setup(); + await agentManager.setup(); + try { + await runner({}); + } finally { + agentManager.cleanup(); + fleetManager.cleanup(); + } +} export async function DefendWorkflowsCypressCliTestRunner(context: FtrProviderContext) { await startDefendWorkflowsCypress(context, 'dw:run'); @@ -19,6 +57,10 @@ export async function DefendWorkflowsCypressVisualTestRunner(context: FtrProvide await startDefendWorkflowsCypress(context, 'dw:open'); } +export async function DefendWorkflowsCypressEndpointTestRunner(context: FtrProviderContext) { + await withFleetAgent(context, () => startDefendWorkflowsCypress(context, 'dw:endpoint:open')); +} + function startDefendWorkflowsCypress(context: FtrProviderContext, cypressCommand: string) { const log = context.getService('log'); const config = context.getService('config'); diff --git a/x-pack/test/defend_workflows_cypress/utils.ts b/x-pack/test/defend_workflows_cypress/utils.ts new file mode 100644 index 0000000000000..2f0b8baa1e856 --- /dev/null +++ b/x-pack/test/defend_workflows_cypress/utils.ts @@ -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 axios from 'axios'; +import semver from 'semver'; +import { filter } from 'lodash'; +import { KbnClient } from '@kbn/test'; + +/** + * Returns the Agent version that is available for install (will check `artifacts-api.elastic.co/v1/versions`) + * that is equal to or less than `maxVersion`. + * @param maxVersion + */ + +export const getLatestAvailableAgentVersion = async (kbnClient: KbnClient): Promise => { + const kbnStatus = await kbnClient.status.get(); + const agentVersions = await axios + .get('https://artifacts-api.elastic.co/v1/versions') + .then((response) => + filter(response.data.versions, (versionString) => !versionString.includes('SNAPSHOT')) + ); + + let version = + semver.maxSatisfying(agentVersions, `<=${kbnStatus.version.number}`) ?? + kbnStatus.version.number; + + // Add `-SNAPSHOT` if version indicates it was from a snapshot or the build hash starts + // with `xxxxxxxxx` (value that seems to be present when running kibana from source) + if ( + kbnStatus.version.build_snapshot || + kbnStatus.version.build_hash.startsWith('XXXXXXXXXXXXXXX') + ) { + version += '-SNAPSHOT'; + } + + return version; +}; From 2205ba74886577f818bd43ece5c0817daea56b5b Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:12:27 +0000 Subject: [PATCH 44/68] [SecuritySolution] Fix index filters (#151069) ## Summary Relevant issues: https://github.com/elastic/kibana/pull/148681/files#r1104594568 https://github.com/elastic/kibana/pull/136407 Please enable feature flag: chartEmbeddablesEnabled ### Steps to verify: 1. Check the selected index patterns you have from the sourcerer 2. Inspect KPIs on host / network / users pages, make sure it matches the selected index patterns from the sourcerer 3. Click `...` button of each KPIs and `Open in Lens`, Inspect > request, and check the displayed index patterns matches index filters. ### Before: Indices were missing from the inspect panel: Screenshot 2023-02-13 at 19 50 47 Index patterns were missing from Lens inspect panel: Screenshot 2023-02-13 at 19 51 14 ### After: Screenshot 2023-02-13 at 19 57 46 Screenshot 2023-02-13 at 19 58 11 ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../search_source/search_source.test.ts | 26 +++++ .../search/search_source/search_source.ts | 18 +++- .../__snapshots__/authentication.test.ts.snap | 34 +++--- .../common/__snapshots__/event.test.ts.snap | 34 +++--- .../__snapshots__/external_alert.test.ts.snap | 34 +++--- .../alerts_by_status_donut.test.ts.snap | 34 +++--- .../alerts_histogram.test.ts.snap | 68 ++++++------ .../__snapshots__/alerts_table.test.ts.snap | 102 +++++++++--------- .../risk_score_over_time_area.test.ts.snap | 12 +-- .../__snapshots__/kpi_host_area.test.ts.snap | 34 +++--- .../kpi_host_metric.test.ts.snap | 34 +++--- .../kpi_unique_ips_area.test.ts.snap | 34 +++--- .../kpi_unique_ips_bar.test.ts.snap | 34 +++--- ...unique_ips_destination_metric.test.ts.snap | 34 +++--- .../kpi_unique_ips_source_metric.test.ts.snap | 34 +++--- .../dns_top_domains.test.ts.snap | 34 +++--- .../kpi_dns_queries.test.ts.snap | 34 +++--- .../kpi_network_events.test.ts.snap | 34 +++--- .../kpi_tls_handshakes.test.ts.snap | 34 +++--- .../kpi_unique_flow_ids.test.ts.snap | 34 +++--- .../kpi_unique_private_ips_area.test.ts.snap | 34 +++--- .../kpi_unique_private_ips_bar.test.ts.snap | 34 +++--- ...rivate_ips_destination_metric.test.ts.snap | 34 +++--- ...que_private_ips_source_metric.test.ts.snap | 34 +++--- .../kpi_total_users_area.test.ts.snap | 34 +++--- .../kpi_total_users_metric.test.ts.snap | 34 +++--- ...authentication_metric_failure.test.ts.snap | 34 +++--- ...kpi_user_authentications_area.test.ts.snap | 34 +++--- .../kpi_user_authentications_bar.test.ts.snap | 34 +++--- ...uthentications_metric_success.test.ts.snap | 34 +++--- .../use_lens_attributes.test.tsx | 6 +- .../use_lens_attributes.tsx | 2 +- 32 files changed, 560 insertions(+), 524 deletions(-) diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index f06a15edf044a..af83fb6c6de4a 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -163,6 +163,32 @@ describe('SearchSource', () => { expect(searchSource.getActiveIndexFilter()).toMatchObject(['auditbeat-*']); }); + test('pass _index from filter - phrases filter', () => { + const filter: Filter[] = [ + { + meta: { + type: 'phrases', + key: '_index', + params: ['auditbeat-*', 'packetbeat-*'], + alias: null, + negate: false, + disabled: false, + }, + query: { + bool: { + should: [ + { match_phrase: { _index: 'auditbeat-*' } }, + { match_phrase: { _index: 'packetbeat-*' } }, + ], + minimum_should_match: 1, + }, + }, + }, + ]; + searchSource.setField('filter', filter); + expect(searchSource.getActiveIndexFilter()).toMatchObject(['auditbeat-*', 'packetbeat-*']); + }); + test('pass _index from query and filter with negate equals to true', () => { const filter = [ { diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 0d79d469f40c8..be7bef0339f3f 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -82,7 +82,13 @@ import { } from 'rxjs/operators'; import { defer, EMPTY, from, lastValueFrom, Observable } from 'rxjs'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { buildEsQuery, Filter, isOfQueryType, isPhraseFilter } from '@kbn/es-query'; +import { + buildEsQuery, + Filter, + isOfQueryType, + isPhraseFilter, + isPhrasesFilter, +} from '@kbn/es-query'; import { fieldWildcardFilter } from '@kbn/kibana-utils-plugin/common'; import { getHighlightRequest } from '@kbn/field-formats-plugin/common'; import type { DataView } from '@kbn/data-views-plugin/common'; @@ -279,12 +285,16 @@ export class SearchSource { }, []) ?? []; const activeIndexPattern = filters?.reduce((acc, f) => { - if (isPhraseFilter(f)) { + const isPhraseFilterType = isPhraseFilter(f); + const isPhrasesFilterType = isPhrasesFilter(f); + const filtersToChange = isPhraseFilterType ? f.meta.params?.query : f.meta.params; + const filtersArray = Array.isArray(filtersToChange) ? filtersToChange : [filtersToChange]; + if (isPhraseFilterType || isPhrasesFilterType) { if (f.meta.key === '_index' && f.meta.disabled === false) { if (f.meta.negate === false) { - return concat(acc, f.meta.params?.query ?? f.meta.params); + return concat(acc, filtersArray); } else { - return difference(acc, [f.meta.params?.query]); + return difference(acc, filtersArray); } } else { return acc; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap index a00e6517914da..7df379780a565 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/authentication.test.ts.snap @@ -124,23 +124,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -204,6 +187,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/event.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/event.test.ts.snap index ab1eed774e8aa..dbe4ca9818123 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/event.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/event.test.ts.snap @@ -68,23 +68,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -148,6 +131,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap index 7d06e63032e95..6c56414f3c05e 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/external_alert.test.ts.snap @@ -104,23 +104,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -184,6 +167,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_by_status_donut.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_by_status_donut.test.ts.snap index 12de83c43fc33..93d3341b066c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_by_status_donut.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_by_status_donut.test.ts.snap @@ -91,23 +91,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -132,6 +115,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "internalReferences": Array [], "query": Object { diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_histogram.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_histogram.test.ts.snap index d0b6f7a79ce34..dda6e51c180a2 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_histogram.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_histogram.test.ts.snap @@ -94,23 +94,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -135,6 +118,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "internalReferences": Array [], "query": Object { @@ -253,23 +253,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -294,6 +277,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "internalReferences": Array [], "query": Object { diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_table.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_table.test.ts.snap index 58ecf5d44d015..c18b413cd2a57 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_table.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/__snapshots__/alerts_table.test.ts.snap @@ -94,23 +94,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -135,6 +118,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "internalReferences": Array [], "query": Object { @@ -284,23 +284,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -325,6 +308,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "internalReferences": Array [], "query": Object { @@ -450,23 +450,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -491,6 +474,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "internalReferences": Array [], "query": Object { diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap index c178855402cc1..8a37b8b9fe54b 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap @@ -96,16 +96,16 @@ Object { "meta": Object { "alias": null, "disabled": false, - "key": "host.id", + "key": "host.name", "negate": false, "params": Object { - "query": "123", + "query": "mockHost", }, "type": "phrase", }, "query": Object { "match_phrase": Object { - "host.id": "123", + "host.name": "mockHost", }, }, }, @@ -113,16 +113,16 @@ Object { "meta": Object { "alias": null, "disabled": false, - "key": "host.name", + "key": "host.id", "negate": false, "params": Object { - "query": "mockHost", + "query": "123", }, "type": "phrase", }, "query": Object { "match_phrase": Object { - "host.name": "mockHost", + "host.id": "123", }, }, }, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap index c8de37bfbd678..dd51f28c6c448 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_area.test.ts.snap @@ -52,23 +52,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -132,6 +115,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap index e4328cb0178e2..1877d7d544a9a 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_host_metric.test.ts.snap @@ -40,23 +40,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -120,6 +103,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap index 18d0e8f86d936..f10087899d289 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_area.test.ts.snap @@ -86,23 +86,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -166,6 +149,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap index ea60bad9c8efd..1f1cdf9bb04d5 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_bar.test.ts.snap @@ -99,23 +99,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -179,6 +162,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap index 48eff6c978975..4dd91ed447904 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_destination_metric.test.ts.snap @@ -40,23 +40,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -120,6 +103,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap index 31f9e00156dd1..7e72be59b669c 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/hosts/__snapshots__/kpi_unique_ips_source_metric.test.ts.snap @@ -40,23 +40,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -120,6 +103,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap index a261abe99ffcf..278b3af5dc411 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/dns_top_domains.test.ts.snap @@ -96,23 +96,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -193,6 +176,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap index 6629abc42f569..4484f4e776dc8 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_dns_queries.test.ts.snap @@ -83,23 +83,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -180,6 +163,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap index e7fd2d8bf70a4..f10f59f8e5a73 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_network_events.test.ts.snap @@ -88,23 +88,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -185,6 +168,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap index 250c1b1d02c35..e28dab070bd3a 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_tls_handshakes.test.ts.snap @@ -107,23 +107,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -204,6 +187,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap index 05209c4a42444..03deef8e5eeb3 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_flow_ids.test.ts.snap @@ -71,23 +71,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -168,6 +151,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap index de3a0dde9ef2f..71722765d8314 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_area.test.ts.snap @@ -93,23 +93,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -190,6 +173,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap index 35f177ca4b357..455356e8cd5ae 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_bar.test.ts.snap @@ -106,23 +106,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -203,6 +186,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap index 6318b784c653e..aadbc3d380a23 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_destination_metric.test.ts.snap @@ -44,23 +44,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -141,6 +124,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap index fae2f1c32e12a..20e51077ade8a 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/network/__snapshots__/kpi_unique_private_ips_source_metric.test.ts.snap @@ -44,23 +44,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -141,6 +124,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap index 3130c179fd779..43bc6ff353a42 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap @@ -52,23 +52,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -110,6 +93,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap index a513615dff945..6e22cc1876d6f 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap @@ -39,23 +39,6 @@ Object { }, }, "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -97,6 +80,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap index b14661069853b..587af046984d6 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap @@ -68,23 +68,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -126,6 +109,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap index ed3c98a263fca..9216cabe9f508 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap @@ -119,23 +119,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -177,6 +160,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap index 421ee7cd49801..5c814accf07c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap @@ -124,23 +124,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -182,6 +165,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap index bb021ebb9abe6..cf82e144bbb09 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap @@ -69,23 +69,6 @@ Object { }, }, }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, Object { "meta": Object { "alias": null, @@ -127,6 +110,23 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": null, + "disabled": false, + "key": "host.id", + "negate": false, + "params": Object { + "query": "123", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "host.id": "123", + }, + }, + }, ], "query": Object { "language": "kql", diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx index 6fd82d4b0e1e9..070bd87ad6d94 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx @@ -69,10 +69,10 @@ describe('useLensAttributes', () => { expect(result?.current?.state.filters).toEqual([ ...getExternalAlertLensAttributes().state.filters, - ...filterFromSearchBar, ...getDetailsPageFilter('hosts', 'mockHost'), ...hostNameExistsFilter, ...getIndexFilters(['auditbeat-*']), + ...filterFromSearchBar, ]); }); @@ -95,10 +95,10 @@ describe('useLensAttributes', () => { expect(result?.current?.state.filters).toEqual([ ...getExternalAlertLensAttributes().state.filters, - ...filterFromSearchBar, ...getNetworkDetailsPageFilter('192.168.1.1'), ...sourceOrDestinationIpExistsFilter, ...getIndexFilters(['auditbeat-*']), + ...filterFromSearchBar, ]); }); @@ -121,9 +121,9 @@ describe('useLensAttributes', () => { expect(result?.current?.state.filters).toEqual([ ...getExternalAlertLensAttributes().state.filters, - ...filterFromSearchBar, ...getDetailsPageFilter('user', 'elastic'), ...getIndexFilters(['auditbeat-*']), + ...filterFromSearchBar, ]); }); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx index 9b5ef16dddd22..2cadf8129ce07 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx @@ -108,10 +108,10 @@ export const useLensAttributes = ({ ...(applyGlobalQueriesAndFilters ? { query } : {}), filters: [ ...attrs.state.filters, - ...(applyGlobalQueriesAndFilters ? filters : []), ...pageFilters, ...tabsFilters, ...indexFilters, + ...(applyGlobalQueriesAndFilters ? filters : []), ], }, references: attrs?.references?.map((ref: { id: string; name: string; type: string }) => ({ From 0a66d93f5c275f181bcd743f90acbf5234155c34 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 15 Feb 2023 20:31:48 +0200 Subject: [PATCH 45/68] [Cloud Security] Showing full `cloud.account.id` for cspm accounts (#151265) --- .../dashboard_sections/cluster_details_box.tsx | 18 ++++++++++++------ .../translations/translations/fr-FR.json | 2 -- .../translations/translations/ja-JP.json | 2 -- .../translations/translations/zh-CN.json | 2 -- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx index 8e8bfc2ac0948..39fb171fbb64a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx @@ -32,14 +32,20 @@ const defaultClusterTitle = i18n.translate( const getClusterTitle = (cluster: Cluster) => { if (cluster.meta.benchmark.posture_type === 'cspm') return cluster.meta.cloud?.account.name; - if (cluster.meta.benchmark.posture_type === 'kspm') return cluster.meta.cluster?.name; + return cluster.meta.cluster?.name; +}; + +const getClusterId = (cluster: Cluster) => { + const assetIdentifierId = cluster.meta.assetIdentifierId; + if (cluster.meta.benchmark.posture_type === 'cspm') return assetIdentifierId; + return assetIdentifierId.slice(0, 6); }; export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => { const { euiTheme } = useEuiTheme(); const navToFindings = useNavigateFindings(); - const shortId = cluster.meta.assetIdentifierId.slice(0, 6); + const assetId = getClusterId(cluster); const title = getClusterTitle(cluster) || defaultClusterTitle; const handleClusterTitleClick = () => { @@ -60,10 +66,10 @@ export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => { @@ -75,10 +81,10 @@ export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => {
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index cbca7476239df..a756443479e41 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -10018,8 +10018,6 @@ "xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundForNameTitle": " pour \"{name}\"", "xpack.csp.benchmarks.totalIntegrationsCountMessage": "Affichage de {pageCount} sur {totalCount, plural, one {# intégration} other {# intégrations}}", "xpack.csp.cloudPosturePage.errorRenderer.errorDescription": "{error} {statusCode} : {body}", - "xpack.csp.dashboard.benchmarkSection.clusterTitle": "{title} - {shortId}", - "xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterTitle": "{title} - {shortId}", "xpack.csp.dashboard.benchmarkSection.lastEvaluatedTitle": "Dernière évaluation {dateFromNow}", "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "Affichage de {pageStart}-{pageEnd} sur {total} {type}", "xpack.csp.findings.findingsTableCell.addFilterButton": "Ajouter un filtre {field}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 89c266124c0d7..5231f0235dfc0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10007,8 +10007,6 @@ "xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundForNameTitle": " \"{name}\"", "xpack.csp.benchmarks.totalIntegrationsCountMessage": "{pageCount}/{totalCount, plural, other {#個の統合}}を表示しています", "xpack.csp.cloudPosturePage.errorRenderer.errorDescription": "{error} {statusCode}: {body}", - "xpack.csp.dashboard.benchmarkSection.clusterTitle": "{title} - {shortId}", - "xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterTitle": "{title} - {shortId}", "xpack.csp.dashboard.benchmarkSection.lastEvaluatedTitle": "前回の評価{dateFromNow}", "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "{total}件中{pageStart}-{pageEnd}件の{type}を表示しています", "xpack.csp.findings.findingsTableCell.addFilterButton": "{field}フィルターを追加", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index bc922fb478e65..a9a79064c06fb 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10022,8 +10022,6 @@ "xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundForNameTitle": " 对于“{name}”", "xpack.csp.benchmarks.totalIntegrationsCountMessage": "正在显示 {pageCount}/{totalCount, plural, other {# 个集成}}", "xpack.csp.cloudPosturePage.errorRenderer.errorDescription": "{error} {statusCode}:{body}", - "xpack.csp.dashboard.benchmarkSection.clusterTitle": "{title} - {shortId}", - "xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterTitle": "{title} - {shortId}", "xpack.csp.dashboard.benchmarkSection.lastEvaluatedTitle": "上次评估于 {dateFromNow}", "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "正在显示第 {pageStart}-{pageEnd} 个(共 {total} 个){type}", "xpack.csp.findings.findingsTableCell.addFilterButton": "添加 {field} 筛选", From 4e1b4c299ce6313edba6dfdcb959ccce142f498f Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Wed, 15 Feb 2023 11:33:39 -0700 Subject: [PATCH 46/68] Core to SharedOX ownership Transfer: Updates code owners for handover to sharedUX (#151213) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 14 +++++++------- .../core-chrome-browser-internal/kibana.jsonc | 2 +- .../core-chrome-browser-internal/package.json | 3 +-- .../chrome/core-chrome-browser-mocks/kibana.jsonc | 2 +- .../chrome/core-chrome-browser-mocks/package.json | 3 +-- .../core/chrome/core-chrome-browser/kibana.jsonc | 2 +- x-pack/plugins/banners/kibana.jsonc | 2 +- x-pack/plugins/global_search/kibana.jsonc | 2 +- x-pack/plugins/global_search_bar/kibana.jsonc | 2 +- .../plugins/global_search_providers/kibana.jsonc | 2 +- 10 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6b230be1aefeb..3dd0eae82192b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -46,7 +46,7 @@ packages/kbn-axe-config @elastic/kibana-qa packages/kbn-babel-preset @elastic/kibana-operations packages/kbn-babel-register @elastic/kibana-operations packages/kbn-babel-transform @elastic/kibana-operations -x-pack/plugins/banners @elastic/kibana-core +x-pack/plugins/banners @elastic/appex-sharedux packages/kbn-bazel-runner @elastic/kibana-operations examples/bfetch_explorer @elastic/appex-sharedux src/plugins/bfetch @elastic/appex-sharedux @@ -113,9 +113,9 @@ packages/core/capabilities/core-capabilities-common @elastic/kibana-core packages/core/capabilities/core-capabilities-server @elastic/kibana-core packages/core/capabilities/core-capabilities-server-internal @elastic/kibana-core packages/core/capabilities/core-capabilities-server-mocks @elastic/kibana-core -packages/core/chrome/core-chrome-browser @elastic/kibana-core -packages/core/chrome/core-chrome-browser-internal @elastic/kibana-core -packages/core/chrome/core-chrome-browser-mocks @elastic/kibana-core +packages/core/chrome/core-chrome-browser @elastic/appex-sharedux +packages/core/chrome/core-chrome-browser-internal @elastic/appex-sharedux +packages/core/chrome/core-chrome-browser-mocks @elastic/appex-sharedux packages/core/config/core-config-server-internal @elastic/kibana-core packages/core/custom-branding/core-custom-branding-browser @elastic/appex-sharedux packages/core/custom-branding/core-custom-branding-browser-internal @elastic/appex-sharedux @@ -373,9 +373,9 @@ packages/kbn-ftr-screenshot-filename @elastic/kibana-operations x-pack/test/functional_with_es_ssl/plugins/cases @elastic/response-ops packages/kbn-generate @elastic/kibana-operations packages/kbn-get-repo-files @elastic/kibana-operations -x-pack/plugins/global_search_bar @elastic/kibana-core -x-pack/plugins/global_search @elastic/kibana-core -x-pack/plugins/global_search_providers @elastic/kibana-core +x-pack/plugins/global_search_bar @elastic/appex-sharedux +x-pack/plugins/global_search @elastic/appex-sharedux +x-pack/plugins/global_search_providers @elastic/appex-sharedux x-pack/test/plugin_functional/plugins/global_search_test @elastic/kibana-core x-pack/plugins/graph @elastic/kibana-visualizations x-pack/plugins/grokdebugger @elastic/platform-deployment-management diff --git a/packages/core/chrome/core-chrome-browser-internal/kibana.jsonc b/packages/core/chrome/core-chrome-browser-internal/kibana.jsonc index 84db611a0b348..da80249250739 100644 --- a/packages/core/chrome/core-chrome-browser-internal/kibana.jsonc +++ b/packages/core/chrome/core-chrome-browser-internal/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", "id": "@kbn/core-chrome-browser-internal", - "owner": "@elastic/kibana-core" + "owner": "@elastic/appex-sharedux" } diff --git a/packages/core/chrome/core-chrome-browser-internal/package.json b/packages/core/chrome/core-chrome-browser-internal/package.json index 51108d8d25023..6239397e9ee04 100644 --- a/packages/core/chrome/core-chrome-browser-internal/package.json +++ b/packages/core/chrome/core-chrome-browser-internal/package.json @@ -2,6 +2,5 @@ "name": "@kbn/core-chrome-browser-internal", "private": true, "version": "1.0.0", - "author": "Kibana Core", "license": "SSPL-1.0 OR Elastic License 2.0" -} \ No newline at end of file +} diff --git a/packages/core/chrome/core-chrome-browser-mocks/kibana.jsonc b/packages/core/chrome/core-chrome-browser-mocks/kibana.jsonc index 84c0c7c07701d..b040712b338fd 100644 --- a/packages/core/chrome/core-chrome-browser-mocks/kibana.jsonc +++ b/packages/core/chrome/core-chrome-browser-mocks/kibana.jsonc @@ -2,5 +2,5 @@ "type": "shared-common", "id": "@kbn/core-chrome-browser-mocks", "devOnly": true, - "owner": "@elastic/kibana-core" + "owner": "@elastic/appex-sharedux" } diff --git a/packages/core/chrome/core-chrome-browser-mocks/package.json b/packages/core/chrome/core-chrome-browser-mocks/package.json index f73bd5dba44f4..ff49363bd75c9 100644 --- a/packages/core/chrome/core-chrome-browser-mocks/package.json +++ b/packages/core/chrome/core-chrome-browser-mocks/package.json @@ -2,6 +2,5 @@ "name": "@kbn/core-chrome-browser-mocks", "private": true, "version": "1.0.0", - "author": "Kibana Core", "license": "SSPL-1.0 OR Elastic License 2.0" -} \ No newline at end of file +} diff --git a/packages/core/chrome/core-chrome-browser/kibana.jsonc b/packages/core/chrome/core-chrome-browser/kibana.jsonc index ea1cb4638d0f9..bac6a50bdbe01 100644 --- a/packages/core/chrome/core-chrome-browser/kibana.jsonc +++ b/packages/core/chrome/core-chrome-browser/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", "id": "@kbn/core-chrome-browser", - "owner": "@elastic/kibana-core" + "owner": "@elastic/appex-sharedux" } diff --git a/x-pack/plugins/banners/kibana.jsonc b/x-pack/plugins/banners/kibana.jsonc index 9f4f661654eed..ffd3ced829961 100644 --- a/x-pack/plugins/banners/kibana.jsonc +++ b/x-pack/plugins/banners/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/banners-plugin", - "owner": "@elastic/kibana-core", + "owner": "@elastic/appex-sharedux", "plugin": { "id": "banners", "server": true, diff --git a/x-pack/plugins/global_search/kibana.jsonc b/x-pack/plugins/global_search/kibana.jsonc index 5c70096b124c1..080d327dec4cb 100644 --- a/x-pack/plugins/global_search/kibana.jsonc +++ b/x-pack/plugins/global_search/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/global-search-plugin", - "owner": "@elastic/kibana-core", + "owner": "@elastic/appex-sharedux", "plugin": { "id": "globalSearch", "server": true, diff --git a/x-pack/plugins/global_search_bar/kibana.jsonc b/x-pack/plugins/global_search_bar/kibana.jsonc index b208f36fdd833..44689ddc5bdfd 100644 --- a/x-pack/plugins/global_search_bar/kibana.jsonc +++ b/x-pack/plugins/global_search_bar/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/global-search-bar-plugin", - "owner": "@elastic/kibana-core", + "owner": "@elastic/appex-sharedux", "plugin": { "id": "globalSearchBar", "server": false, diff --git a/x-pack/plugins/global_search_providers/kibana.jsonc b/x-pack/plugins/global_search_providers/kibana.jsonc index 5ee9946d172d0..cdfed2ebbaf5d 100644 --- a/x-pack/plugins/global_search_providers/kibana.jsonc +++ b/x-pack/plugins/global_search_providers/kibana.jsonc @@ -1,7 +1,7 @@ { "type": "plugin", "id": "@kbn/global-search-providers-plugin", - "owner": "@elastic/kibana-core", + "owner": "@elastic/appex-sharedux", "plugin": { "id": "globalSearchProviders", "server": true, From a4de864acce376bc1478a0a307f6dd88acb6b9d2 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Wed, 15 Feb 2023 13:43:17 -0500 Subject: [PATCH 47/68] feat(slo): support apm latency indicator type (#150785) --- .../kbn-slo-schema/src/schema/indicators.ts | 2 +- x-pack/plugins/observability/dev_docs/slo.md | 6 +- .../public/data/slo/indicator.ts | 2 +- .../use_fetch_apm_suggestions.ts | 19 +++ .../hooks/slo/use_fetch_apm_suggestions.ts | 76 +++++++++++ ...m_latency_indicator_type_form.stories.tsx} | 15 +-- .../apm_latency_indicator_type_form.tsx | 121 ++++++++++++++++++ .../apm_latency/field_selector.stories.tsx | 40 ++++++ .../components/apm_latency/field_selector.tsx | 121 ++++++++++++++++++ ...custom_kql_indicator_type_form.stories.tsx | 34 +++++ .../custom_kql_indicator_type_form.tsx} | 45 +++---- .../components/custom_kql/index_selection.tsx | 2 + .../components/custom_kql/query_builder.tsx | 2 + .../slo_edit/components/slo_edit_form.tsx | 58 ++++++--- .../components/slo_edit_form_description.tsx | 5 +- .../components/slo_edit_form_objectives.tsx | 6 +- .../slo_edit_form_objectives_timeslices.tsx | 6 +- .../public/pages/slo_edit/constants.ts | 19 ++- .../use_check_form_partial_validities.ts | 41 ------ .../helpers/use_section_form_validation.ts | 73 +++++++++++ .../public/pages/slo_edit/index.test.tsx | 21 +-- .../kibana_react.storybook_decorator.tsx | 2 +- .../server/services/slo/find_slo.test.ts | 2 +- .../server/services/slo/fixtures/slo.ts | 2 +- .../apm_transaction_duration.ts | 3 +- 25 files changed, 598 insertions(+), 125 deletions(-) create mode 100644 x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.ts create mode 100644 x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts rename x-pack/plugins/observability/public/pages/slo_edit/components/{slo_edit_form_definition_custom_kql.stories.tsx => apm_latency/apm_latency_indicator_type_form.stories.tsx} (63%) create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx rename x-pack/plugins/observability/public/pages/slo_edit/components/{slo_edit_form_definition_custom_kql.tsx => custom_kql/custom_kql_indicator_type_form.tsx} (59%) delete mode 100644 x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/helpers/use_section_form_validation.ts diff --git a/packages/kbn-slo-schema/src/schema/indicators.ts b/packages/kbn-slo-schema/src/schema/indicators.ts index 73933fb785794..2e20713708617 100644 --- a/packages/kbn-slo-schema/src/schema/indicators.ts +++ b/packages/kbn-slo-schema/src/schema/indicators.ts @@ -18,7 +18,7 @@ const apmTransactionDurationIndicatorSchema = t.type({ service: allOrAnyString, transactionType: allOrAnyString, transactionName: allOrAnyString, - 'threshold.us': t.number, + threshold: t.number, }), t.partial({ index: t.string, diff --git a/x-pack/plugins/observability/dev_docs/slo.md b/x-pack/plugins/observability/dev_docs/slo.md index a1a303a672afd..bd9dcc96c4c59 100644 --- a/x-pack/plugins/observability/dev_docs/slo.md +++ b/x-pack/plugins/observability/dev_docs/slo.md @@ -181,7 +181,7 @@ curl --request POST \ "service": "o11y-app", "transactionType": "request", "transactionName": "GET /api", - "threshold.us": 500000 + "threshold": 500000 } }, "timeWindow": { @@ -216,7 +216,7 @@ curl --request POST \ "service": "o11y-app", "transactionType": "request", "transactionName": "GET /api", - "threshold.us": 500000 + "threshold": 500000 } }, "timeWindow": { @@ -253,7 +253,7 @@ curl --request POST \ "service": "o11y-app", "transactionType": "request", "transactionName": "GET /api", - "threshold.us": 500000 + "threshold": 500000 } }, "timeWindow": { diff --git a/x-pack/plugins/observability/public/data/slo/indicator.ts b/x-pack/plugins/observability/public/data/slo/indicator.ts index 8e38f787c1b83..6bb36ca54d8c2 100644 --- a/x-pack/plugins/observability/public/data/slo/indicator.ts +++ b/x-pack/plugins/observability/public/data/slo/indicator.ts @@ -33,7 +33,7 @@ export const buildApmLatencyIndicator = ( service: 'o11y-app', transactionType: 'request', transactionName: 'GET /slow', - 'threshold.us': 5000000, + threshold: 5000000, ...params, }, }; diff --git a/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.ts b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.ts new file mode 100644 index 0000000000000..5d5e65e8e114b --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/__storybook_mocks__/use_fetch_apm_suggestions.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 { Params, UseFetchApmSuggestions } from '../use_fetch_apm_suggestions'; + +export const useFetchApmSuggestions = ({ + fieldName, + search = '', +}: Params): UseFetchApmSuggestions => { + return { + loading: false, + error: false, + data: ['apm-suggestion-1', 'apm-suggestion-2', 'apm-suggestion-3'], + }; +}; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts new file mode 100644 index 0000000000000..eca7eeb2b7be9 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_apm_suggestions.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpSetup } from '@kbn/core-http-browser'; +import moment from 'moment'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDataFetcher } from '../use_data_fetcher'; + +export type Suggestion = string; +export interface UseFetchApmSuggestions { + data: Suggestion[]; + loading: boolean; + error: boolean; +} + +export interface Params { + fieldName: string; + search: string; +} + +interface ApiResponse { + terms: string[]; +} + +const EMPTY_RESPONSE: ApiResponse = { terms: [] }; + +export function useFetchApmSuggestions({ fieldName, search = '' }: Params): UseFetchApmSuggestions { + const [suggestions, setSuggestions] = useState([]); + + const params: Params = useMemo(() => ({ fieldName, search }), [fieldName, search]); + const shouldExecuteApiCall = useCallback( + (apiCallParams: Params) => !apiCallParams.search || apiCallParams.search.length > 0, + [] + ); + + const { data, loading, error } = useDataFetcher({ + paramsForApiCall: params, + initialDataState: EMPTY_RESPONSE, + executeApiCall: fetchHistoricalSummary, + shouldExecuteApiCall, + }); + + useEffect(() => { + setSuggestions(data.terms); + }, [data]); + + return { data: suggestions, loading, error }; +} + +const fetchHistoricalSummary = async ( + params: Params, + abortController: AbortController, + http: HttpSetup +): Promise => { + try { + const response = await http.get('/internal/apm/suggestions', { + query: { + fieldName: params.fieldName, + start: moment().subtract(2, 'days').toISOString(), + end: moment().toISOString(), + fieldValue: params.search, + }, + signal: abortController.signal, + }); + + return response; + } catch (error) { + // ignore error for retrieving slos + } + + return { terms: [] }; +}; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx similarity index 63% rename from x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.stories.tsx rename to x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx index 08c634b45faaf..7b98c21b6e5e4 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.stories.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.stories.tsx @@ -9,16 +9,13 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; import { FormProvider, useForm } from 'react-hook-form'; -import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; -import { - SloEditFormDefinitionCustomKql as Component, - Props, -} from './slo_edit_form_definition_custom_kql'; -import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; +import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; +import { ApmLatencyIndicatorTypeForm as Component, Props } from './apm_latency_indicator_type_form'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; export default { component: Component, - title: 'app/SLO/EditPage/SloEditFormDefinitionCustomKql', + title: 'app/SLO/EditPage/ApmLatency/Form', decorators: [KibanaReactStorybookDecorator], }; @@ -33,5 +30,5 @@ const Template: ComponentStory = (props: Props) => { const defaultProps = {}; -export const SloEditFormDefinitionCustomKql = Template.bind({}); -SloEditFormDefinitionCustomKql.args = defaultProps; +export const Form = Template.bind({}); +Form.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx new file mode 100644 index 0000000000000..d071dd92de358 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx @@ -0,0 +1,121 @@ +/* + * 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 { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormLabel } from '@elastic/eui'; +import { Control, Controller } from 'react-hook-form'; +import { i18n } from '@kbn/i18n'; +import type { CreateSLOInput } from '@kbn/slo-schema'; + +import { FieldSelector } from './field_selector'; + +export interface Props { + control: Control; +} + +export function ApmLatencyIndicatorTypeForm({ control }: Props) { + return ( + + + + + + + + + + + + + + + {i18n.translate('xpack.observability.slos.sloEdit.apmLatency.threshold.placeholder', { + defaultMessage: 'Threshold (ms)', + })} + + ( + field.onChange(Number(event.target.value))} + /> + )} + /> + + + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.stories.tsx new file mode 100644 index 0000000000000..28f98a39f9762 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.stories.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { ComponentStory } from '@storybook/react'; + +import { FormProvider, useForm } from 'react-hook-form'; +import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; +import { FieldSelector as Component, Props } from './field_selector'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/ApmLatency/FieldSelector', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps: Omit = { + dataTestSubj: 'dataTestSubj', + name: 'name' as const, + placeholder: 'Select the APM service', + fieldName: 'service.name', + label: 'Service name', +}; + +export const FieldSelector = Template.bind({}); +FieldSelector.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.tsx new file mode 100644 index 0000000000000..af3ff50954757 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/field_selector.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexItem, EuiFormLabel } from '@elastic/eui'; +import { Control, Controller, FieldPath } from 'react-hook-form'; +import { CreateSLOInput } from '@kbn/slo-schema'; +import { i18n } from '@kbn/i18n'; +import { + Suggestion, + useFetchApmSuggestions, +} from '../../../../hooks/slo/use_fetch_apm_suggestions'; + +interface Option { + label: string; + value: string; +} + +export interface Props { + allowAllOption?: boolean; + control: Control; + dataTestSubj: string; + fieldName: string; + label: string; + name: FieldPath; + placeholder: string; +} + +export function FieldSelector({ + allowAllOption = true, + control, + dataTestSubj, + fieldName, + label, + name, + placeholder, +}: Props) { + const [search, setSearch] = useState(''); + const { data: suggestions, loading } = useFetchApmSuggestions({ + fieldName, + search, + }); + const [options, setOptions] = useState([]); + + useEffect(() => { + const opts = ( + allowAllOption + ? [ + { + value: '*', + label: i18n.translate('xpack.observability.slos.sloEdit.apmFieldSelector.all', { + defaultMessage: 'All', + }), + }, + ] + : [] + ).concat(createOptions(suggestions)); + + setOptions(opts); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [suggestions.length]); + + return ( + + {label} + + ( + { + if (selected.length) { + return field.onChange(selected[0].value); + } + + field.onChange(''); + }} + onSearchChange={(value: string) => { + setSearch(value); + }} + options={options} + placeholder={placeholder} + selectedOptions={ + !!field.value && typeof field.value === 'string' + ? [ + { + value: field.value, + label: field.value, + 'data-test-subj': `${dataTestSubj}SelectedValue`, + }, + ] + : [] + } + singleSelection + /> + )} + /> + + ); +} + +function createOptions(suggestions: Suggestion[]): Option[] { + return suggestions + .map((suggestion) => ({ label: suggestion, value: suggestion })) + .sort((a, b) => String(a.label).localeCompare(b.label)); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.tsx new file mode 100644 index 0000000000000..375b133adfb43 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.stories.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 { ComponentStory } from '@storybook/react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import { KibanaReactStorybookDecorator } from '../../../../utils/kibana_react.storybook_decorator'; +import { CustomKqlIndicatorTypeForm as Component, Props } from './custom_kql_indicator_type_form'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/CustomKQL/Form', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps = {}; + +export const Form = Template.bind({}); +Form.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx similarity index 59% rename from x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx rename to x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx index 29d3386f30ad4..07b8bf4d192eb 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx @@ -11,15 +11,15 @@ import { i18n } from '@kbn/i18n'; import { Control, UseFormWatch } from 'react-hook-form'; import type { CreateSLOInput } from '@kbn/slo-schema'; -import { IndexSelection } from './custom_kql/index_selection'; -import { QueryBuilder } from './custom_kql/query_builder'; +import { IndexSelection } from './index_selection'; +import { QueryBuilder } from './query_builder'; export interface Props { control: Control; watch: UseFormWatch; } -export function SloEditFormDefinitionCustomKql({ control, watch }: Props) { +export function CustomKqlIndicatorTypeForm({ control, watch }: Props) { return ( @@ -29,17 +29,14 @@ export function SloEditFormDefinitionCustomKql({ control, watch }: Props) { ( diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx index 455e36d2e818e..f1faba1119652 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx @@ -24,8 +24,8 @@ import type { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useKibana } from '../../../utils/kibana_react'; import { useCreateOrUpdateSlo } from '../../../hooks/slo/use_create_slo'; -import { useCheckFormPartialValidities } from '../helpers/use_check_form_partial_validities'; -import { SloEditFormDefinitionCustomKql } from './slo_edit_form_definition_custom_kql'; +import { useSectionFormValidation } from '../helpers/use_section_form_validation'; +import { CustomKqlIndicatorTypeForm } from './custom_kql/custom_kql_indicator_type_form'; import { SloEditFormDescription } from './slo_edit_form_description'; import { SloEditFormObjectives } from './slo_edit_form_objectives'; import { @@ -35,6 +35,7 @@ import { } from '../helpers/process_slo_form_values'; import { paths } from '../../../config'; import { SLI_OPTIONS, SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; +import { ApmLatencyIndicatorTypeForm } from './apm_latency/apm_latency_indicator_type_form'; export interface Props { slo: SLOWithSummaryResponse | undefined; @@ -56,9 +57,13 @@ export function SloEditForm({ slo }: Props) { mode: 'all', }); - const { isDefinitionValid, isDescriptionValid, isObjectiveValid } = useCheckFormPartialValidities( - { getFieldState, formState } - ); + const { isIndicatorSectionValid, isDescriptionSectionValid, isObjectiveSectionValid } = + useSectionFormValidation({ + getFieldState, + getValues, + formState, + watch, + }); const { loading, success, error, createSlo, updateSlo } = useCreateOrUpdateSlo(); @@ -99,15 +104,28 @@ export function SloEditForm({ slo }: Props) { } }, [success, error, toasts, isEditMode, getValues, navigateToUrl, basePath]); + const getIndicatorTypeForm = () => { + switch (watch('indicator.type')) { + case 'sli.kql.custom': + return ; + case 'sli.apm.transactionDuration': + return ; + default: + return null; + } + }; + return ( } > @@ -132,7 +150,7 @@ export function SloEditForm({ slo }: Props) { name="indicator.type" control={control} rules={{ required: true }} - render={({ field }) => ( + render={({ field: { ref, ...field } }) => ( - {watch('indicator.type') === 'sli.kql.custom' ? ( - - ) : null} + {getIndicatorTypeForm()} @@ -155,9 +171,11 @@ export function SloEditForm({ slo }: Props) { verticalAlign="top" icon={ } > @@ -182,9 +200,13 @@ export function SloEditForm({ slo }: Props) { verticalAlign="top" icon={ } > diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx index c169b3316a681..15e13c53862b3 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx @@ -39,7 +39,7 @@ export function SloEditFormDescription({ control }: Props) { name="name" control={control} rules={{ required: true }} - render={({ field }) => ( + render={({ field: { ref, ...field } }) => ( ( + render={({ field: { ref, ...field } }) => ( ( + render={({ field: { ref, ...field } }) => ( ( + render={({ field: { ref, ...field } }) => ( ( + render={({ field: { ref, ...field } }) => ( ( + render={({ field: { ref, ...field } }) => ( ( + render={({ field: { ref, ...field } }) => ( = [ { - value: 'sli.kql.custom' as const, - text: i18n.translate('xpack.observability.slos.sloTypes.kqlCustomIndicator', { - defaultMessage: 'KQL custom indicator', + value: 'sli.kql.custom', + text: i18n.translate('xpack.observability.slos.sliTypes.kqlCustomIndicator', { + defaultMessage: 'KQL custom', + }), + }, + { + value: 'sli.apm.transactionDuration', + text: i18n.translate('xpack.observability.slos.sliTypes.apmLatencyIndicator', { + defaultMessage: 'APM latency', }), }, ]; @@ -44,7 +53,7 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES: CreateSLOInput = { name: '', description: '', indicator: { - type: SLI_OPTIONS[0].value, + type: 'sli.kql.custom', params: { index: '', filter: '', diff --git a/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts b/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts deleted file mode 100644 index 392cfd0ef5204..0000000000000 --- a/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts +++ /dev/null @@ -1,41 +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 { CreateSLOInput } from '@kbn/slo-schema'; -import { FormState, UseFormGetFieldState } from 'react-hook-form'; - -interface Props { - getFieldState: UseFormGetFieldState; - formState: FormState; -} - -export function useCheckFormPartialValidities({ getFieldState, formState }: Props) { - const isDefinitionValid = ( - [ - 'indicator.params.index', - 'indicator.params.filter', - 'indicator.params.good', - 'indicator.params.total', - ] as const - ).every((field) => getFieldState(field, formState).error === undefined); - - const isObjectiveValid = ( - [ - 'budgetingMethod', - 'timeWindow.duration', - 'objective.target', - 'objective.timesliceTarget', - 'objective.timesliceWindow', - ] as const - ).every((field) => getFieldState(field, formState).error === undefined); - - const isDescriptionValid = (['name', 'description'] as const).every( - (field) => getFieldState(field, formState).error === undefined - ); - - return { isDefinitionValid, isObjectiveValid, isDescriptionValid }; -} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_section_form_validation.ts b/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_section_form_validation.ts new file mode 100644 index 0000000000000..0f258b6102cbe --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_section_form_validation.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CreateSLOInput } from '@kbn/slo-schema'; +import { FormState, UseFormGetFieldState, UseFormGetValues, UseFormWatch } from 'react-hook-form'; + +interface Props { + getFieldState: UseFormGetFieldState; + getValues: UseFormGetValues; + formState: FormState; + watch: UseFormWatch; +} + +export function useSectionFormValidation({ getFieldState, getValues, formState, watch }: Props) { + let isIndicatorSectionValid: boolean; + switch (watch('indicator.type')) { + case 'sli.kql.custom': + isIndicatorSectionValid = + ( + [ + 'indicator.params.index', + 'indicator.params.filter', + 'indicator.params.good', + 'indicator.params.total', + ] as const + ).every((field) => !getFieldState(field, formState).invalid) && + getValues('indicator.params.index') !== ''; + break; + case 'sli.apm.transactionDuration': + isIndicatorSectionValid = + ( + [ + 'indicator.params.service', + 'indicator.params.environment', + 'indicator.params.transactionType', + 'indicator.params.transactionName', + 'indicator.params.threshold', + ] as const + ).every((field) => !getFieldState(field, formState).invalid && getValues(field) !== '') && + (['indicator.params.index'] as const).every( + (field) => !getFieldState(field, formState).invalid + ); + break; + default: + isIndicatorSectionValid = false; + break; + } + + const isObjectiveSectionValid = ( + [ + 'budgetingMethod', + 'timeWindow.duration', + 'objective.target', + 'objective.timesliceTarget', + 'objective.timesliceWindow', + ] as const + ).every((field) => getFieldState(field, formState).error === undefined); + + const isDescriptionSectionValid = + !getFieldState('name', formState).invalid && + getValues('name') !== '' && + !getFieldState('description', formState).invalid; + + return { + isIndicatorSectionValid, + isObjectiveSectionValid, + isDescriptionSectionValid, + }; +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx b/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx index e9baaf105e10a..00330a2fe1356 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx @@ -195,17 +195,17 @@ describe('SLO Edit Page', () => { expect(screen.queryByTestId('indexSelectionSelectedValue')).toBeNull(); - expect(screen.queryByTestId('sloFormCustomKqlFilterQueryInput')).toHaveValue( + expect(screen.queryByTestId('customKqlIndicatorFormQueryFilterInput')).toHaveValue( SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.filter : '' ); - expect(screen.queryByTestId('sloFormCustomKqlGoodQueryInput')).toHaveValue( + expect(screen.queryByTestId('customKqlIndicatorFormGoodQueryInput')).toHaveValue( SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.good : '' ); - expect(screen.queryByTestId('sloFormCustomKqlTotalQueryInput')).toHaveValue( + expect(screen.queryByTestId('customKqlIndicatorFormTotalQueryInput')).toHaveValue( SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.total : '' @@ -249,9 +249,12 @@ describe('SLO Edit Page', () => { render(, config); userEvent.type(screen.getByTestId('indexSelection'), 'some-index'); - userEvent.type(screen.getByTestId('sloFormCustomKqlFilterQueryInput'), 'irrelevant'); - userEvent.type(screen.getByTestId('sloFormCustomKqlGoodQueryInput'), 'irrelevant'); - userEvent.type(screen.getByTestId('sloFormCustomKqlTotalQueryInput'), 'irrelevant'); + userEvent.type( + screen.getByTestId('customKqlIndicatorFormQueryFilterInput'), + 'irrelevant' + ); + userEvent.type(screen.getByTestId('customKqlIndicatorFormGoodQueryInput'), 'irrelevant'); + userEvent.type(screen.getByTestId('customKqlIndicatorFormTotalQueryInput'), 'irrelevant'); userEvent.selectOptions( screen.getByTestId('sloFormBudgetingMethodSelect'), 'occurrences' @@ -337,13 +340,13 @@ describe('SLO Edit Page', () => { slo.indicator.params.index! ); - expect(screen.queryByTestId('sloFormCustomKqlFilterQueryInput')).toHaveValue( + expect(screen.queryByTestId('customKqlIndicatorFormQueryFilterInput')).toHaveValue( slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.filter : '' ); - expect(screen.queryByTestId('sloFormCustomKqlGoodQueryInput')).toHaveValue( + expect(screen.queryByTestId('customKqlIndicatorFormGoodQueryInput')).toHaveValue( slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.good : '' ); - expect(screen.queryByTestId('sloFormCustomKqlTotalQueryInput')).toHaveValue( + expect(screen.queryByTestId('customKqlIndicatorFormTotalQueryInput')).toHaveValue( slo.indicator.type === 'sli.kql.custom' ? slo.indicator.params.total : '' ); diff --git a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx index bf84059db3b6a..2882b8491f390 100644 --- a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx +++ b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx @@ -22,7 +22,7 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { }, data: {}, dataViews: { - create: () => {}, + create: () => Promise.resolve({}), }, docLinks: { links: { diff --git a/x-pack/plugins/observability/server/services/slo/find_slo.test.ts b/x-pack/plugins/observability/server/services/slo/find_slo.test.ts index ea1a18c0c178e..a0b6eca9b064c 100644 --- a/x-pack/plugins/observability/server/services/slo/find_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/find_slo.test.ts @@ -53,7 +53,7 @@ describe('FindSLO', () => { service: 'irrelevant', transactionName: 'irrelevant', transactionType: 'irrelevant', - 'threshold.us': 500000, + threshold: 500, }, type: 'sli.apm.transactionDuration', }, diff --git a/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts b/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts index 008004369fe83..c65c515521ca3 100644 --- a/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts +++ b/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts @@ -48,7 +48,7 @@ export const createAPMTransactionDurationIndicator = ( service: 'irrelevant', transactionName: 'irrelevant', transactionType: 'irrelevant', - 'threshold.us': 500000, + threshold: 500, ...params, }, }); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts index 89e8b962d61af..7893e7daa6799 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts @@ -111,7 +111,8 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator } private buildAggregations(slo: SLO, indicator: APMTransactionDurationIndicator) { - const truncatedThreshold = Math.trunc(indicator.params['threshold.us']); + // threshold is in ms (milliseconds), but apm data is stored in us (microseconds) + const truncatedThreshold = Math.trunc(indicator.params.threshold * 1000); return { _numerator: { From 0757c69a79382010fc2a6ddfc601fb2bfe35fbc6 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Wed, 15 Feb 2023 13:47:47 -0500 Subject: [PATCH 48/68] feat(slo): Add custom kql filter into APM indicator types (#150936) --- .../kbn-slo-schema/src/schema/indicators.ts | 2 + .../slo_transform_template.ts | 2 + .../apm_transaction_duration.test.ts.snap | 96 +++++++++++++++++++ .../apm_transaction_error_rate.test.ts.snap | 96 +++++++++++++++++++ .../__snapshots__/kql_custom.test.ts.snap | 2 + .../apm_transaction_duration.test.ts | 12 +++ .../apm_transaction_duration.ts | 7 +- .../apm_transaction_error_rate.test.ts | 12 +++ .../apm_transaction_error_rate.ts | 7 +- .../slo/transform_generators/common.ts | 17 ++++ .../slo/transform_generators/index.ts | 1 + .../slo/transform_generators/kql_custom.ts | 12 +-- .../transform_generator.ts | 4 + 13 files changed, 258 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/observability/server/services/slo/transform_generators/common.ts diff --git a/packages/kbn-slo-schema/src/schema/indicators.ts b/packages/kbn-slo-schema/src/schema/indicators.ts index 2e20713708617..045d267b04d42 100644 --- a/packages/kbn-slo-schema/src/schema/indicators.ts +++ b/packages/kbn-slo-schema/src/schema/indicators.ts @@ -22,6 +22,7 @@ const apmTransactionDurationIndicatorSchema = t.type({ }), t.partial({ index: t.string, + filter: t.string, }), ]), }); @@ -41,6 +42,7 @@ const apmTransactionErrorRateIndicatorSchema = t.type({ t.union([t.literal('2xx'), t.literal('3xx'), t.literal('4xx'), t.literal('5xx')]) ), index: t.string, + filter: t.string, }), ]), }); diff --git a/x-pack/plugins/observability/server/assets/transform_templates/slo_transform_template.ts b/x-pack/plugins/observability/server/assets/transform_templates/slo_transform_template.ts index d9a8bfb1de8cb..799a991ef7971 100644 --- a/x-pack/plugins/observability/server/assets/transform_templates/slo_transform_template.ts +++ b/x-pack/plugins/observability/server/assets/transform_templates/slo_transform_template.ts @@ -21,6 +21,7 @@ export interface TransformSettings { export const getSLOTransformTemplate = ( transformId: string, + description: string, source: TransformSource, destination: TransformDestination, groupBy: TransformPivot['group_by'] = {}, @@ -28,6 +29,7 @@ export const getSLOTransformTemplate = ( settings: TransformSettings ): TransformPutTransformRequest => ({ transform_id: transformId, + description, source, frequency: settings.frequency, dest: destination, diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap index 1b8a2e6336e91..66339b775fa97 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap @@ -1,5 +1,99 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`APM Transaction Duration Transform Generator adds the custom kql filter to the query 1`] = ` +Object { + "bool": Object { + "filter": Array [ + Object { + "match": Object { + "transaction.root": true, + }, + }, + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-7d", + }, + }, + }, + Object { + "match": Object { + "service.name": "irrelevant", + }, + }, + Object { + "match": Object { + "service.environment": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.name": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.type": "irrelevant", + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "my.field": "value", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "range": Object { + "foo": Object { + "gte": "12", + }, + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "range": Object { + "bar": Object { + "lte": "100", + }, + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, +} +`; + exports[`APM Transaction Duration Transform Generator does not include the query filter when params are '*' 1`] = ` Object { "bool": Object { @@ -26,6 +120,7 @@ Object { "_meta": Object { "version": 1, }, + "description": "Rolled-up SLI data for SLO: irrelevant", "dest": Object { "index": "slo-observability.sli-v1", "pipeline": "slo-observability.sli.monthly", @@ -158,6 +253,7 @@ Object { "_meta": Object { "version": 1, }, + "description": "Rolled-up SLI data for SLO: irrelevant", "dest": Object { "index": "slo-observability.sli-v1", "pipeline": "slo-observability.sli.monthly", diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap index 76657144e11f0..e9d4e4107b472 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap @@ -1,5 +1,99 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`APM Transaction Error Rate Transform Generator adds the custom kql filter to the query 1`] = ` +Object { + "bool": Object { + "filter": Array [ + Object { + "match": Object { + "transaction.root": true, + }, + }, + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-7d", + }, + }, + }, + Object { + "match": Object { + "service.name": "irrelevant", + }, + }, + Object { + "match": Object { + "service.environment": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.name": "irrelevant", + }, + }, + Object { + "match": Object { + "transaction.type": "irrelevant", + }, + }, + Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match_phrase": Object { + "my.field": "value", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "range": Object { + "foo": Object { + "gte": "12", + }, + }, + }, + ], + }, + }, + Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "range": Object { + "bar": Object { + "lte": "100", + }, + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, +} +`; + exports[`APM Transaction Error Rate Transform Generator does not include the query filter when params are '*' 1`] = ` Object { "bool": Object { @@ -26,6 +120,7 @@ Object { "_meta": Object { "version": 1, }, + "description": "Rolled-up SLI data for SLO: irrelevant", "dest": Object { "index": "slo-observability.sli-v1", "pipeline": "slo-observability.sli.monthly", @@ -163,6 +258,7 @@ Object { "_meta": Object { "version": 1, }, + "description": "Rolled-up SLI data for SLO: irrelevant", "dest": Object { "index": "slo-observability.sli-v1", "pipeline": "slo-observability.sli.monthly", diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap index fbfa14ecfb56b..afe345d2b7261 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap @@ -105,6 +105,7 @@ Object { "_meta": Object { "version": 1, }, + "description": "Rolled-up SLI data for SLO: irrelevant", "dest": Object { "index": "slo-observability.sli-v1", "pipeline": "slo-observability.sli.monthly", @@ -209,6 +210,7 @@ Object { "_meta": Object { "version": 1, }, + "description": "Rolled-up SLI data for SLO: irrelevant", "dest": Object { "index": "slo-observability.sli-v1", "pipeline": "slo-observability.sli.monthly", diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts index 16a788888cc09..82a1dd1c65652 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts @@ -69,4 +69,16 @@ describe('APM Transaction Duration Transform Generator', () => { expect(transform.source.index).toEqual(index); }); + + it('adds the custom kql filter to the query', async () => { + const filter = `"my.field" : "value" and ("foo" >= 12 or "bar" <= 100)`; + const anSLO = createSLO({ + indicator: createAPMTransactionDurationIndicator({ + filter, + }), + }); + const transform = generator.getTransformParams(anSLO); + + expect(transform.source.query).toMatchSnapshot(); + }); }); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts index 7893e7daa6799..6914ca19c586c 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.ts @@ -19,7 +19,7 @@ import { } from '../../../assets/constants'; import { getSLOTransformTemplate } from '../../../assets/transform_templates/slo_transform_template'; import { SLO, APMTransactionDurationIndicator } from '../../../domain/models'; -import { TransformGenerator } from '.'; +import { getElastichsearchQueryOrThrow, TransformGenerator } from '.'; import { DEFAULT_APM_INDEX } from './constants'; import { Query } from './types'; @@ -31,6 +31,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator return getSLOTransformTemplate( this.buildTransformId(slo), + this.buildDescription(slo), this.buildSource(slo, slo.indicator), this.buildDestination(), this.buildCommonGroupBy(slo), @@ -85,6 +86,10 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator }); } + if (!!indicator.params.filter) { + queryFilter.push(getElastichsearchQueryOrThrow(indicator.params.filter)); + } + return { index: indicator.params.index ?? DEFAULT_APM_INDEX, runtime_mappings: this.buildCommonRuntimeMappings(slo), diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts index 1763cff5fab6d..50931ad3fe95b 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts @@ -78,4 +78,16 @@ describe('APM Transaction Error Rate Transform Generator', () => { expect(transform.source.index).toEqual(index); }); + + it('adds the custom kql filter to the query', async () => { + const filter = `"my.field" : "value" and ("foo" >= 12 or "bar" <= 100)`; + const anSLO = createSLO({ + indicator: createAPMTransactionErrorRateIndicator({ + filter, + }), + }); + const transform = generator.getTransformParams(anSLO); + + expect(transform.source.query).toMatchSnapshot(); + }); }); diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts index 8adcbcdfc9622..69bd394a6800d 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.ts @@ -14,7 +14,7 @@ import { import { InvalidTransformError } from '../../../errors'; import { getSLOTransformTemplate } from '../../../assets/transform_templates/slo_transform_template'; -import { TransformGenerator } from '.'; +import { getElastichsearchQueryOrThrow, TransformGenerator } from '.'; import { SLO_DESTINATION_INDEX_NAME, SLO_INGEST_PIPELINE_NAME, @@ -35,6 +35,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato return getSLOTransformTemplate( this.buildTransformId(slo), + this.buildDescription(slo), this.buildSource(slo, slo.indicator), this.buildDestination(), this.buildCommonGroupBy(slo), @@ -90,6 +91,10 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato }); } + if (indicator.params.filter) { + queryFilter.push(getElastichsearchQueryOrThrow(indicator.params.filter)); + } + return { index: indicator.params.index ?? DEFAULT_APM_INDEX, runtime_mappings: this.buildCommonRuntimeMappings(slo), diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/common.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/common.ts new file mode 100644 index 0000000000000..b9426ef5f5a77 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/common.ts @@ -0,0 +1,17 @@ +/* + * 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 { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { InvalidTransformError } from '../../../errors'; + +export function getElastichsearchQueryOrThrow(kuery: string) { + try { + return toElasticsearchQuery(fromKueryExpression(kuery)); + } catch (err) { + throw new InvalidTransformError(`Invalid KQL: ${kuery}`); + } +} diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/index.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/index.ts index 08d6032a6eafa..a7d1e33c7303e 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/index.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/index.ts @@ -9,3 +9,4 @@ export * from './transform_generator'; export * from './apm_transaction_error_rate'; export * from './apm_transaction_duration'; export * from './kql_custom'; +export * from './common'; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.ts index 2b69c02e9d2c5..70709e53c313c 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.ts @@ -6,12 +6,11 @@ */ import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { kqlCustomIndicatorSchema, timeslicesBudgetingMethodSchema } from '@kbn/slo-schema'; import { InvalidTransformError } from '../../../errors'; import { getSLOTransformTemplate } from '../../../assets/transform_templates/slo_transform_template'; -import { TransformGenerator } from '.'; +import { getElastichsearchQueryOrThrow, TransformGenerator } from '.'; import { SLO_DESTINATION_INDEX_NAME, SLO_INGEST_PIPELINE_NAME, @@ -27,6 +26,7 @@ export class KQLCustomTransformGenerator extends TransformGenerator { return getSLOTransformTemplate( this.buildTransformId(slo), + this.buildDescription(slo), this.buildSource(slo, slo.indicator), this.buildDestination(), this.buildCommonGroupBy(slo), @@ -80,11 +80,3 @@ export class KQLCustomTransformGenerator extends TransformGenerator { }; } } - -function getElastichsearchQueryOrThrow(kuery: string) { - try { - return toElasticsearchQuery(fromKueryExpression(kuery)); - } catch (err) { - throw new InvalidTransformError(`Invalid KQL: ${kuery}`); - } -} diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts index da88c48d5479c..c4f9c426d1cd0 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts @@ -32,6 +32,10 @@ export abstract class TransformGenerator { }; } + public buildDescription(slo: SLO): string { + return `Rolled-up SLI data for SLO: ${slo.name}`; + } + public buildCommonGroupBy(slo: SLO) { let fixedInterval = '1m'; if (timeslicesBudgetingMethodSchema.is(slo.budgetingMethod)) { From 6a4bdf247f49f9b6599767f1f804f5a60d0d7e99 Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Wed, 15 Feb 2023 20:11:06 +0100 Subject: [PATCH 49/68] update dependency playwright to ^1.30.0 (#151106) ## Summary Currently we use 1.27.1 that comes with bundled Chromium 107.0.5304.18 (released September 28, 2022) Updating playwright to use the recent [Chromium 110](https://github.com/microsoft/playwright/releases/tag/v1.30.0) for testing. --- package.json | 2 +- yarn.lock | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 61441db1fd8ef..21159e2e9d3a7 100644 --- a/package.json +++ b/package.json @@ -1421,7 +1421,7 @@ "pirates": "^4.0.1", "piscina": "^3.2.0", "pixelmatch": "^5.3.0", - "playwright": "^1.26.0", + "playwright": "^1.30.0", "pngjs": "^3.4.0", "postcss": "^8.4.14", "postcss-loader": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index 164ecb0e54d82..145fc8c6280d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22744,12 +22744,17 @@ playwright-core@1.27.1, playwright-core@=1.27.1: resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.27.1.tgz#840ef662e55a3ed759d8b5d3d00a5f885a7184f4" integrity sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q== -playwright@^1.26.0: - version "1.27.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.27.1.tgz#4eecac5899566c589d4220ca8acc16abe8a67450" - integrity sha512-xXYZ7m36yTtC+oFgqH0eTgullGztKSRMb4yuwLPl8IYSmgBM88QiB+3IWb1mRIC9/NNwcgbG0RwtFlg+EAFQHQ== +playwright-core@1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.30.0.tgz#de987cea2e86669e3b85732d230c277771873285" + integrity sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g== + +playwright@^1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.30.0.tgz#b1d7be2d45d97fbb59f829f36f521f12010fe072" + integrity sha512-ENbW5o75HYB3YhnMTKJLTErIBExrSlX2ZZ1C/FzmHjUYIfxj/UnI+DWpQr992m+OQVSg0rCExAOlRwB+x+yyIg== dependencies: - playwright-core "1.27.1" + playwright-core "1.30.0" plugin-error@^1.0.1: version "1.0.1" From d0ec60de308deaf95baef24e9d3d73012d4fb997 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Wed, 15 Feb 2023 21:15:12 +0200 Subject: [PATCH 50/68] [Cloud Security] Add CTA to `findings delayed` state (#151325) --- .../public/components/no_findings_states.tsx | 13 ++++++++++++- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx index 04346d284724d..bb1ed64a89123 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx @@ -12,6 +12,7 @@ import { EuiEmptyPrompt, EuiIcon, EuiMarkdownFormat, + EuiLink, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -118,7 +119,17 @@ const IndexTimeout = () => (

+ + + ), + }} />

} diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index a756443479e41..5a609c618f48c 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -10139,7 +10139,6 @@ "xpack.csp.navigation.rulesNavItemLabel": "Règles", "xpack.csp.noFindingsStates.indexing.indexingButtonTitle": "Pas encore de résultats", "xpack.csp.noFindingsStates.indexing.indexingDescription": "En attente de la collecte et de l'indexation des données. Revenez plus tard pour voir vos résultats", - "xpack.csp.noFindingsStates.indexTimeout.indexTimeoutDescription": "La collecte des résultats prend plus de temps que prévu, revenez nous voir bientôt.", "xpack.csp.noFindingsStates.indexTimeout.indexTimeoutTitle": "Résultats retardés", "xpack.csp.noFindingsStates.noAgentsDeployed.noAgentsDeployedButtonTitle": "Installer l'agent", "xpack.csp.noFindingsStates.noAgentsDeployed.noAgentsDeployedDescription": "Pour voir les résultats, terminez le processus de configuration en installant un agent Elastic sur votre cluster Kubernetes.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5231f0235dfc0..400dba199ca16 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10128,7 +10128,6 @@ "xpack.csp.navigation.rulesNavItemLabel": "ルール", "xpack.csp.noFindingsStates.indexing.indexingButtonTitle": "まだ結果がありません", "xpack.csp.noFindingsStates.indexing.indexingDescription": "データの収集とインデックス作成を待機しています。結果を表示するには、しばらくたってから確認してください", - "xpack.csp.noFindingsStates.indexTimeout.indexTimeoutDescription": "結果の収集に想定よりも時間がかかっています。後でもう一度確認してください", "xpack.csp.noFindingsStates.indexTimeout.indexTimeoutTitle": "結果が遅れています", "xpack.csp.noFindingsStates.noAgentsDeployed.noAgentsDeployedButtonTitle": "エージェントのインストール", "xpack.csp.noFindingsStates.noAgentsDeployed.noAgentsDeployedDescription": "結果を表示するには、KubernetesクラスターでElasticエージェントをインストールし、セットアップ処理を完了してください。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a9a79064c06fb..1ecd40512117e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10143,7 +10143,6 @@ "xpack.csp.navigation.rulesNavItemLabel": "规则", "xpack.csp.noFindingsStates.indexing.indexingButtonTitle": "尚无结果", "xpack.csp.noFindingsStates.indexing.indexingDescription": "正在等待要收集和索引的数据。请稍后返回检查以查看结果", - "xpack.csp.noFindingsStates.indexTimeout.indexTimeoutDescription": "收集结果所需的时间长于预期,请稍后再回来检查", "xpack.csp.noFindingsStates.indexTimeout.indexTimeoutTitle": "已推迟结果", "xpack.csp.noFindingsStates.noAgentsDeployed.noAgentsDeployedButtonTitle": "安装代理", "xpack.csp.noFindingsStates.noAgentsDeployed.noAgentsDeployedDescription": "要查看结果,请通过在 Kubernetes 集群上安装 Elastic 代理来完成设置过程。", From af25a3e0737deeacc678d89497136047e8c1c8fc Mon Sep 17 00:00:00 2001 From: Cee Chen <549407+cee-chen@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:17:29 -0800 Subject: [PATCH 51/68] Upgrade EUI to v75.1.0 (#151200) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `eui@75.0.0` ⏩ `eui@75.1.0` --- ## [`75.1.0`](https://github.com/elastic/eui/tree/v75.1.0) - Added padding to `EuiStep` title to better align with icon ([#6555](https://github.com/elastic/eui/pull/6555)) - Added a new `lineNumbers.annotations` API to `EuiCodeBlock`. This new feature displays an informational icon next to the specified line number(s), providing more context via popover ([#6580](https://github.com/elastic/eui/pull/6580)) **Bug fixes** - Fixed bug in `EuiRange` where styles were applied incorrectly when custom ticks were passed but `showTicks` were false ([#6588](https://github.com/elastic/eui/pull/6588)) - Fixed `fleetApp` and `agentApp` icons that were swapped ([#6590](https://github.com/elastic/eui/pull/6590)) **CSS-in-JS conversions** - Converted `EuiSteps` to Emotion; Removed `$euiStepStatusColorsToFade`, `$euiStepNumberSize`, `$euiStepNumberSmallSize`, and `$euiStepNumberMargin` ([#6555](https://github.com/elastic/eui/pull/6555)) --- package.json | 2 +- .../src/__snapshots__/i18n_service.test.tsx.snap | 1 + .../core-i18n-browser-internal/src/i18n_eui_mapping.tsx | 7 +++++++ src/dev/license_checker/config.ts | 2 +- .../components/horizontal_page_steps.tsx | 2 +- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 21159e2e9d3a7..24aba91e5f45a 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.6.0-canary.3", "@elastic/ems-client": "8.4.0", - "@elastic/eui": "75.0.0", + "@elastic/eui": "75.1.0", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap index 95c1b8fed4a6a..69e50df7cd4b8 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap +++ b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap @@ -27,6 +27,7 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiCardSelect.select": "Select", "euiCardSelect.selected": "Selected", "euiCardSelect.unavailable": "Unavailable", + "euiCodeBlockAnnotations.ariaLabel": [Function], "euiCodeBlockCopy.copy": "Copy", "euiCodeBlockFullScreen.fullscreenCollapse": "Collapse", "euiCodeBlockFullScreen.fullscreenExpand": "Expand", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx index f3a29731325f2..5a50a74cc0f19 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx +++ b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx @@ -125,6 +125,13 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: 'Unavailable', description: 'Displayed button text when a card option is unavailable.', }), + 'euiCodeBlockAnnotations.ariaLabel': ({ lineNumber }: EuiValues) => + i18n.translate('core.euiCodeBlockAnnotations.ariaLabel', { + defaultMessage: 'Click to view a code annotation for line {lineNumber}', + values: { lineNumber }, + description: + 'ARIA label for a button icon that toggles a popover annotation for a specific code line', + }), 'euiCodeBlockCopy.copy': i18n.translate('core.euiCodeBlockCopy.copy', { defaultMessage: 'Copy', description: 'ARIA label for a button that copies source code text to the clipboard', diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index dd3e6f17c80f1..dd0600b71d2b2 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -84,6 +84,6 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.4.0': ['Elastic License 2.0'], - '@elastic/eui@75.0.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@75.1.0': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/horizontal_page_steps.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/horizontal_page_steps.tsx index 8e33dcff5616e..752b49431dadb 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/horizontal_page_steps.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/horizontal_page_steps.tsx @@ -28,7 +28,7 @@ const NumberlessHorizontalSteps = styled(EuiStepsHorizontal)` .euiStepHorizontal { padding: 25px 16px 16px; } - .euiStepHorizontal-isIncomplete .euiStepHorizontal__title { + .euiStepHorizontal[data-step-status='incomplete'] .euiStepHorizontal__title { color: #69707d; } `; diff --git a/yarn.lock b/yarn.lock index 145fc8c6280d5..18f9a96fe74c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1539,10 +1539,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@75.0.0": - version "75.0.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-75.0.0.tgz#06f1d41181327d91d6531f240372c01c34505bee" - integrity sha512-fgjpF4l+YuMVepnYbiddpVZHkzEVrgwvsmJokaf0OZZ34UCh+OB2z4n01yAhmhQXhrFEKv8+LmQt+OMIpTBevA== +"@elastic/eui@75.1.0": + version "75.1.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-75.1.0.tgz#6bdb2a12e5dd503258e74d5585803f52b826b83e" + integrity sha512-HJgoARNsXeYDIGO9sKV+wwfmFA2IKL9hjOMj8B0PZ4fA6Euprw7KPLkakUbwjTCm0rqYUf/6zmXRafvzvdKLmA== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" From 467891e7608b7c4d500693e2fd97d40da268952a Mon Sep 17 00:00:00 2001 From: Alexi Doak <109488926+doakalexi@users.noreply.github.com> Date: Wed, 15 Feb 2023 12:53:38 -0800 Subject: [PATCH 52/68] [ResponseOps] [Alerting] Add warning to Edit Rule Flyout when publicUrl is not configured (#149832) Resolves https://github.com/elastic/kibana/issues/144890 ## Summary Added a warning to the rule flyout that will warn users about using `context.alertDetailsUrl` and `context.viewInAppUrl` in an input or text area when they do not have `publicUrl` set in their kibana config. **Warning:** Screen Shot 2023-02-01 at 11 18 17 AM Screen Shot 2023-02-01 at 11 18 26 AM ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### To verify - Remove your kibana config for `server.publicBaseUrl`, and create a rule with a connector. Add the context variables `context.alertDetailsUrl` or `context.viewInAppUrl` and verify that a warning shows up in the form. - Add `server.publicBaseUrl` to your kibana config, and create a rule with a connector. Add the context variables `context.alertDetailsUrl` or `context.viewInAppUrl` and verify that a warning shows does **not** show in the form. - You should try this for multiple connectors to verify that the warnings are configured correctly for them. --- x-pack/plugins/alerting/common/rule.ts | 1 + .../server/routes/alerts/action_variables.ts | 2 + ...er_inventory_metric_threshold_rule_type.ts | 14 ++++- .../register_log_threshold_rule_type.ts | 9 +++- .../register_metric_threshold_rule_type.ts | 14 ++++- .../rule_types/es_query/rule_type.test.ts | 1 + .../server/rule_types/es_query/rule_type.ts | 2 +- .../server/alert_rules/action_variables.ts | 2 + .../lib/alerts/action_variables.ts | 2 + .../application/lib/action_variables.test.ts | 2 + .../application/lib/action_variables.ts | 1 + .../lib/validate_params_for_warnings.test.ts | 54 +++++++++++++++++++ .../lib/validate_params_for_warnings.ts | 45 ++++++++++++++++ .../action_type_form.test.tsx | 7 ++- .../action_type_form.tsx | 34 +++++++++++- 15 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.test.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 49c274f140ea9..f548441713420 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -202,6 +202,7 @@ export interface ActionVariable { description: string; deprecated?: boolean; useWithTripleBracesInTemplates?: boolean; + usesPublicBaseUrl?: boolean; } export interface RuleMonitoringHistory extends SavedObjectAttributes { diff --git a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts index bf0453811b205..0df3d7f9d7b71 100644 --- a/x-pack/plugins/apm/server/routes/alerts/action_variables.ts +++ b/x-pack/plugins/apm/server/routes/alerts/action_variables.ts @@ -17,6 +17,7 @@ export const apmActionVariables = { } ), name: 'alertDetailsUrl' as const, + usesPublicBaseUrl: true, }, environment: { description: i18n.translate( @@ -84,5 +85,6 @@ export const apmActionVariables = { } ), name: 'viewInAppUrl' as const, + usesPublicBaseUrl: true, }, }; diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts index 998a0d7051f0a..77fa814a622a4 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts @@ -118,14 +118,24 @@ export async function registerMetricInventoryThresholdRuleType( { name: 'group', description: groupActionVariableDescription }, { name: 'alertState', description: alertStateActionVariableDescription }, ...(getAlertDetailsPageEnabledForApp(config, 'metrics') - ? [{ name: 'alertDetailsUrl', description: alertDetailUrlActionVariableDescription }] + ? [ + { + name: 'alertDetailsUrl', + description: alertDetailUrlActionVariableDescription, + usesPublicBaseUrl: true, + }, + ] : []), { name: 'reason', description: reasonActionVariableDescription }, { name: 'timestamp', description: timestampActionVariableDescription }, { name: 'value', description: valueActionVariableDescription }, { name: 'metric', description: metricActionVariableDescription }, { name: 'threshold', description: thresholdActionVariableDescription }, - { name: 'viewInAppUrl', description: viewInAppUrlActionVariableDescription }, + { + name: 'viewInAppUrl', + description: viewInAppUrlActionVariableDescription, + usesPublicBaseUrl: true, + }, { name: 'cloud', description: cloudActionVariableDescription }, { name: 'host', description: hostActionVariableDescription }, { name: 'container', description: containerActionVariableDescription }, diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts index 09f46c98c1719..297416d0d6632 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_rule_type.ts @@ -144,11 +144,18 @@ export async function registerLogThresholdRuleType( description: denominatorConditionsActionVariableDescription, }, ...(getAlertDetailsPageEnabledForApp(config, 'logs') - ? [{ name: 'alertDetailsUrl', description: alertDetailUrlActionVariableDescription }] + ? [ + { + name: 'alertDetailsUrl', + description: alertDetailUrlActionVariableDescription, + usesPublicBaseUrl: true, + }, + ] : []), { name: 'viewInAppUrl', description: viewInAppUrlActionVariableDescription, + usesPublicBaseUrl: true, }, { name: 'cloud', description: cloudActionVariableDescription }, { name: 'host', description: hostActionVariableDescription }, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts index 2b7074ac2956e..d2d0d36f42ac5 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts @@ -150,7 +150,13 @@ export async function registerMetricThresholdRuleType( { name: 'group', description: groupActionVariableDescription }, { name: 'groupByKeys', description: groupByKeysActionVariableDescription }, ...(getAlertDetailsPageEnabledForApp(config, 'metrics') - ? [{ name: 'alertDetailsUrl', description: alertDetailUrlActionVariableDescription }] + ? [ + { + name: 'alertDetailsUrl', + description: alertDetailUrlActionVariableDescription, + usesPublicBaseUrl: true, + }, + ] : []), { name: 'alertState', description: alertStateActionVariableDescription }, { name: 'reason', description: reasonActionVariableDescription }, @@ -158,7 +164,11 @@ export async function registerMetricThresholdRuleType( { name: 'value', description: valueActionVariableDescription }, { name: 'metric', description: metricActionVariableDescription }, { name: 'threshold', description: thresholdActionVariableDescription }, - { name: 'viewInAppUrl', description: viewInAppUrlActionVariableDescription }, + { + name: 'viewInAppUrl', + description: viewInAppUrlActionVariableDescription, + usesPublicBaseUrl: true, + }, { name: 'cloud', description: cloudActionVariableDescription }, { name: 'host', description: hostActionVariableDescription }, { name: 'container', description: containerActionVariableDescription }, diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts index cc6c4414777ea..aa0b5d8a4d1fc 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts @@ -67,6 +67,7 @@ describe('ruleType', () => { "description": "Navigate to Discover and show the records that triggered the alert when the rule is created in Discover. Otherwise, navigate to the status page for the rule.", "name": "link", + "usesPublicBaseUrl": true, }, ], "params": Array [ diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts index c56f691cc2580..214d2ee4b764e 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.ts @@ -150,7 +150,7 @@ export function getRuleType( { name: 'value', description: actionVariableContextValueLabel }, { name: 'hits', description: actionVariableContextHitsLabel }, { name: 'conditions', description: actionVariableContextConditionsLabel }, - { name: 'link', description: actionVariableContextLinkLabel }, + { name: 'link', description: actionVariableContextLinkLabel, usesPublicBaseUrl: true }, ], params: [ { name: 'size', description: actionVariableContextSizeLabel }, diff --git a/x-pack/plugins/synthetics/server/alert_rules/action_variables.ts b/x-pack/plugins/synthetics/server/alert_rules/action_variables.ts index 4e73a3b3660ab..3e70b51298ec8 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/action_variables.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/action_variables.ts @@ -58,6 +58,7 @@ export const ACTION_VARIABLES = { defaultMessage: 'Link to a view showing further details and context on this alert', } ), + usesPublicBaseUrl: true, }, [VIEW_IN_APP_URL]: { name: VIEW_IN_APP_URL, @@ -67,6 +68,7 @@ export const ACTION_VARIABLES = { defaultMessage: 'Open alert details and context in Synthetics app.', } ), + usesPublicBaseUrl: true, }, [RECOVERY_REASON]: { name: RECOVERY_REASON, diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/action_variables.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/action_variables.ts index a498274f7455e..b3cd8d5817ff8 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/action_variables.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/action_variables.ts @@ -40,6 +40,7 @@ export const ACTION_VARIABLES = { 'Link to the view within Elastic that shows further details and context surrounding this alert', } ), + usesPublicBaseUrl: true, }, [VIEW_IN_APP_URL]: { name: VIEW_IN_APP_URL, @@ -50,5 +51,6 @@ export const ACTION_VARIABLES = { 'Link to the view or feature within Elastic that can be used to investigate the alert and its context further', } ), + usesPublicBaseUrl: true, }, }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts index c7641d3437a2c..ac94547851155 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts @@ -47,6 +47,7 @@ const expectedTransformResult = [ description: 'The URL to the Stack Management rule page that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.', name: 'rule.url', + usesPublicBaseUrl: true, }, { description: 'The date the rule scheduled the action.', name: 'date' }, { description: 'The ID of the alert that scheduled actions for the rule.', name: 'alert.id' }, @@ -172,6 +173,7 @@ const expectedSummaryTransformResult = [ description: 'The URL to the Stack Management rule page that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.', name: 'rule.url', + usesPublicBaseUrl: true, }, { description: 'The tags of the rule.', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts index cc85d4adbb068..76b6aba407771 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts @@ -116,6 +116,7 @@ const AlertProvidedActionVariableDescriptions = { defaultMessage: 'The URL to the Stack Management rule page that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.', }), + usesPublicBaseUrl: true, }, [AlertProvidedActionVariables.date]: { name: AlertProvidedActionVariables.date, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.test.ts new file mode 100644 index 0000000000000..4f3d627c4fcf8 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.test.ts @@ -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 { ActionVariable } from '@kbn/alerting-plugin/common'; +import { validateParamsForWarnings } from './validate_params_for_warnings'; + +describe('validateParamsForWarnings', () => { + const actionVariables: ActionVariable[] = [ + { + name: 'context.url', + description: 'Test url', + usesPublicBaseUrl: true, + }, + { + name: 'context.name', + description: 'Test name', + }, + ]; + + test('returns warnings when publicUrl is not set and there are publicUrl variables used', () => { + const warning = 'server.publicBaseUrl is not set. Actions will use relative URLs.'; + expect( + validateParamsForWarnings('Test for {{context.url}}', undefined, actionVariables) + ).toEqual(warning); + + expect( + validateParamsForWarnings('link: {{ context.url }}', undefined, actionVariables) + ).toEqual(warning); + + expect( + validateParamsForWarnings('{{=<% %>=}}link: <%context.url%>', undefined, actionVariables) + ).toEqual(warning); + }); + + test('does not return warnings when publicUrl is not set and there are publicUrl variables not used', () => { + expect( + validateParamsForWarnings('Test for {{context.name}}', undefined, actionVariables) + ).toBeFalsy(); + }); + + test('does not return warnings when publicUrl is set and there are publicUrl variables used', () => { + expect( + validateParamsForWarnings('Test for {{context.url}}', 'http://test', actionVariables) + ).toBeFalsy(); + }); + + test('does not returns warnings when publicUrl is not set and the value is not a string', () => { + expect(validateParamsForWarnings(10, undefined, actionVariables)).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts new file mode 100644 index 0000000000000..fe7d2b0e5ffb5 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/validate_params_for_warnings.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { some } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { ActionVariable, RuleActionParam } from '@kbn/alerting-plugin/common'; +import Mustache from 'mustache'; + +const publicUrlWarning = i18n.translate( + 'xpack.triggersActionsUI.sections.actionTypeForm.warning.publicUrl', + { + defaultMessage: 'server.publicBaseUrl is not set. Actions will use relative URLs.', + } +); + +export function validateParamsForWarnings( + value: RuleActionParam, + publicBaseUrl: string | undefined, + actionVariables: ActionVariable[] | undefined +): string | null { + if (!publicBaseUrl && value && typeof value === 'string') { + const publicUrlFields = (actionVariables || []).reduce((acc, v) => { + if (v.usesPublicBaseUrl) { + acc.push(v.name.replace(/^(params\.|context\.|state\.)/, '')); + acc.push(v.name); + } + return acc; + }, new Array()); + + const variables = new Set( + (Mustache.parse(value) as Array<[string, string]>) + .filter(([type]) => type === 'name') + .map(([, v]) => v) + ); + const hasUrlFields = some(publicUrlFields, (publicUrlField) => variables.has(publicUrlField)); + if (hasUrlFields) { + return publicUrlWarning; + } + } + return null; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index 73f0810d4f98d..d2a443ee208a3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -24,9 +24,10 @@ import { DEFAULT_FREQUENCY } from '../../../common/constants'; import { transformActionVariables } from '../../lib/action_variables'; import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; -jest.mock('../../../common/lib/kibana'); const actionTypeRegistry = actionTypeRegistryMock.create(); +jest.mock('../../../common/lib/kibana'); + jest.mock('../../lib/action_variables', () => { const original = jest.requireActual('../../lib/action_variables'); return { @@ -36,6 +37,10 @@ jest.mock('../../lib/action_variables', () => { }); describe('action_type_form', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const mockedActionParamsFields = React.lazy(async () => ({ default() { return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 11e987f87a8ce..9e42979f1305c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -27,6 +27,7 @@ import { EuiBetaBadge, EuiSplitPanel, useEuiTheme, + EuiCallOut, } from '@elastic/eui'; import { isEmpty, partition, some } from 'lodash'; import { ActionVariable, RuleActionParam } from '@kbn/alerting-plugin/common'; @@ -52,6 +53,7 @@ import { transformActionVariables } from '../../lib/action_variables'; import { useKibana } from '../../../common/lib/kibana'; import { ConnectorsSelection } from './connectors_selection'; import { ActionNotifyWhen } from './action_notify_when'; +import { validateParamsForWarnings } from '../../lib/validate_params_for_warnings'; export type ActionTypeFormProps = { actionItem: RuleAction; @@ -114,6 +116,7 @@ export const ActionTypeForm = ({ }: ActionTypeFormProps) => { const { application: { capabilities }, + http: { basePath }, } = useKibana().services; const { euiTheme } = useEuiTheme(); const [isOpen, setIsOpen] = useState(true); @@ -137,6 +140,8 @@ export const ActionTypeForm = ({ -1, 's', ]; + const [warning, setWarning] = useState(null); + const [useDefaultMessage, setUseDefaultMessage] = useState(false); const isSummaryAction = actionItem.frequency?.summary; @@ -382,7 +387,16 @@ export const ActionTypeForm = ({ actionParams={actionItem.params as any} index={index} errors={actionParamsErrors.errors} - editAction={setActionParamsProperty} + editAction={(key: string, value: RuleActionParam, i: number) => { + setWarning( + validateParamsForWarnings( + value, + basePath.publicBaseUrl, + availableActionVariables + ) + ); + setActionParamsProperty(key, value, i); + }} messageVariables={availableActionVariables} defaultMessage={ // if action is a summary action, show the default summary message @@ -394,6 +408,12 @@ export const ActionTypeForm = ({ actionConnector={actionConnector} executionMode={ActionConnectorMode.ActionForm} /> + {warning ? ( + <> + + + + ) : null} ) : null} @@ -475,6 +495,18 @@ export const ActionTypeForm = ({
)} + {warning && !isOpen && ( + + + {i18n.translate( + 'xpack.triggersActionsUI.sections.actionTypeForm.actionWarningsTitle', + { + defaultMessage: '1 warning', + } + )} + + + )} {checkEnabledResult.isEnabled === false && ( <> From e87d3a151c6e54cfbb0e8bf426279ebfcf4d27af Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau Date: Wed, 15 Feb 2023 17:13:44 -0500 Subject: [PATCH 53/68] [RAM] bug on _run_soon API (#151218) ## Summary FIX https://github.com/elastic/kibana/issues/149432 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../alerting.test.ts | 8 ++ .../feature_privilege_builder/alerting.ts | 1 + .../group3/tests/alerting/index.ts | 1 + .../group3/tests/alerting/run_soon.ts | 75 +++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/run_soon.ts diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts index cc7c9c9e6f402..ce4f25d4e47ea 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts @@ -91,6 +91,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/runSoon", ] `); }); @@ -177,6 +178,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/runSoon", "alerting:1.0.0-zeta1:alert-type/my-feature/alert/get", "alerting:1.0.0-zeta1:alert-type/my-feature/alert/find", "alerting:1.0.0-zeta1:alert-type/my-feature/alert/getAuthorizedAlertsIndices", @@ -223,6 +225,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/runSoon", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -326,6 +329,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/runSoon", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -389,6 +393,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/runSoon", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -411,6 +416,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/runSoon", ] `); }); @@ -502,6 +508,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:alert-type/my-feature/rule/runSoon", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/create", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/delete", "alerting:1.0.0-zeta1:alert-type/my-feature/rule/update", @@ -524,6 +531,7 @@ describe(`feature_privilege_builder`, () => { "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getExecutionLog", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/find", "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/getRuleExecutionKPI", + "alerting:1.0.0-zeta1:readonly-alert-type/my-feature/rule/runSoon", "alerting:1.0.0-zeta1:another-alert-type/my-feature/alert/get", "alerting:1.0.0-zeta1:another-alert-type/my-feature/alert/find", "alerting:1.0.0-zeta1:another-alert-type/my-feature/alert/getAuthorizedAlertsIndices", diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts index 647464c714325..612981c9ffb0c 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts @@ -24,6 +24,7 @@ const readOperations: Record = { 'getExecutionLog', 'find', 'getRuleExecutionKPI', + 'runSoon', ], alert: ['get', 'find', 'getAuthorizedAlertsIndices', 'getAlertSummary'], }; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts index 0c6b4f815c9dc..8f6cbe1a60c89 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/index.ts @@ -26,6 +26,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./bulk_disable')); loadTestFile(require.resolve('./clone')); loadTestFile(require.resolve('./get_flapping_settings')); + loadTestFile(require.resolve('./run_soon')); loadTestFile(require.resolve('./update_flapping_settings')); }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/run_soon.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/run_soon.ts new file mode 100644 index 0000000000000..831edffbae51a --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/run_soon.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { UserAtSpaceScenarios } from '../../../scenarios'; +import { + getTestRuleData, + getConsumerUnauthorizedErrorMessage, + getUrlPrefix, + ObjectRemover, +} from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function createAlertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('runSoon', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(async () => { + await objectRemover.removeAll(); + }); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + describe(scenario.id, () => { + it('should handle run soon rule request appropriately', async () => { + const responseRule = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestRuleData()); + + const response = await supertestWithoutAuth + .post( + `${getUrlPrefix(space.id)}/internal/alerting/rule/${responseRule.body.id}/_run_soon` + ) + .set('kbn-xsrf', 'foo') + .auth(user.username, user.password) + .send(); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: getConsumerUnauthorizedErrorMessage( + 'runSoon', + 'test.noop', + 'alertsFixture' + ), + statusCode: 403, + }); + break; + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode === 200 || response.statusCode === 204).to.be(true); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + }); + } + }); +} From 70f6eb2b4d2abf426d2c1e5bcf6ca630f1f0802a Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Wed, 15 Feb 2023 16:18:34 -0600 Subject: [PATCH 54/68] [TIP] fix policies not shown in add to blocklist flyout (#151088) https://github.com/elastic/kibana/issues/150848 --- .../modules/block_list/containers/flyout.tsx | 5 +- .../block_list/hooks/use_policies.test.tsx | 51 +++++++++++++++++++ .../modules/block_list/hooks/use_policies.ts | 38 ++++++++++++++ .../threat_intelligence/public/types.ts | 4 +- 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.test.tsx create mode 100644 x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.ts diff --git a/x-pack/plugins/threat_intelligence/public/modules/block_list/containers/flyout.tsx b/x-pack/plugins/threat_intelligence/public/modules/block_list/containers/flyout.tsx index 225e990a5e8c3..fd8c1a81fa02f 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/block_list/containers/flyout.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/block_list/containers/flyout.tsx @@ -10,6 +10,7 @@ import { CreateExceptionListItemSchema, EntriesArray, } from '@kbn/securitysolution-io-ts-list-types'; +import { usePolicies } from '../hooks/use_policies'; import { useBlockListContext } from '../../indicators/hooks/use_block_list_context'; import { ADD_TO_BLOCKLIST_FLYOUT_TITLE } from './translations'; import { useSecurityContext } from '../../../hooks/use_security_context'; @@ -33,6 +34,7 @@ export const BlockListFlyout: VFC = ({ indicatorFileHash } const Component = blockList.getFlyoutComponent(); const exceptionListApiClient = blockList.exceptionListApiClient; const FormComponent = blockList.getFormComponent(); + const { isLoading: policiesIsLoading, data: policies } = usePolicies(); // prepopulate the for with the indicator file hash const entries: EntriesArray = [ @@ -67,7 +69,8 @@ export const BlockListFlyout: VFC = ({ indicatorFileHash } apiClient: exceptionListApiClient, labels, item, - policies: [], + policies: policies || [], + policiesIsLoading, FormComponent, onClose: clearBlockListIndicatorValue, }; diff --git a/x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.test.tsx new file mode 100644 index 0000000000000..a01b7da64b684 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.test.tsx @@ -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. + */ + +import React from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'; + +const createWrapper = () => { + const queryClient = new QueryClient(); + return ({ children }: { children: any }) => ( + {children} + ); +}; + +const renderUseQuery = (result: { items: any[] }) => + renderHook(() => useQuery(['policies'], () => result), { + wrapper: createWrapper(), + }); + +describe('usePolicies', () => { + it('should have undefined data during loading state', async () => { + const mockPolicies = { items: [] }; + const { result, waitFor } = renderUseQuery(mockPolicies); + + await waitFor(() => result.current.isLoading); + + expect(result.current.isLoading).toBeTruthy(); + expect(result.current.data).toBeUndefined(); + }); + + it('should return policies on success', async () => { + const mockPolicies = { + items: [ + { + id: '123', + name: 'MyPolicy', + }, + ], + }; + const { result, waitFor } = renderUseQuery(mockPolicies); + + await waitFor(() => result.current.isSuccess); + + expect(result.current.isLoading).toBeFalsy(); + expect(result.current.data).toEqual(mockPolicies); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.ts b/x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.ts new file mode 100644 index 0000000000000..38c5219f6bd25 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/block_list/hooks/use_policies.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useQuery } from '@tanstack/react-query'; +import { useKibana } from '../../../hooks'; + +const POLICIES_URL = '/api/fleet/package_policies'; +const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies'; + +export interface PolicyResponse { + items: Policy[]; +} + +export interface Policy { + id: string; + name: string; +} + +export function usePolicies() { + const { http } = useKibana().services; + const queryKey = ['policies']; + + const fetchPolicies = () => + http.get(POLICIES_URL, { + query: { + withAgentCount: true, + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, + }, + }); + + return useQuery(queryKey, fetchPolicies, { + select: (data: PolicyResponse) => data.items, + }); +} diff --git a/x-pack/plugins/threat_intelligence/public/types.ts b/x-pack/plugins/threat_intelligence/public/types.ts index 3aa0912bd8b9e..8ba584ecb924a 100644 --- a/x-pack/plugins/threat_intelligence/public/types.ts +++ b/x-pack/plugins/threat_intelligence/public/types.ts @@ -23,6 +23,7 @@ import { DataProvider } from '@kbn/timelines-plugin/common'; import { Start as InspectorPluginStart } from '@kbn/inspector-plugin/public'; import { CasesUiSetup, CasesUiStart } from '@kbn/cases-plugin/public/types'; import { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { Policy } from './modules/block_list/hooks/use_policies'; export interface SecuritySolutionDataViewBase extends DataViewBase { fields: Array; @@ -79,7 +80,8 @@ export interface UseInvestigateInTimelineProps { export interface BlockListFlyoutProps { apiClient: unknown; item: CreateExceptionListItemSchema; - policies: unknown[]; + policies: Policy[]; + policiesIsLoading: boolean; FormComponent: NamedExoticComponent; onClose: () => void; } From 4525c4b4e47a7267de8d5c6d30a3a2f668dd2425 Mon Sep 17 00:00:00 2001 From: Davis McPhee Date: Wed, 15 Feb 2023 19:53:23 -0400 Subject: [PATCH 55/68] [Discover] Fix issue where editing a data view causes the UI to become out of sync (#150830) ## Summary Let's try this again with a simpler and hopefully more robust approach. This PR fixes the issue where editing a data view in Discover causes the UI to become out of sync with the current data view due to the stable object identity. Fixes #149857. Fixes #150740. ### Checklist - [ ] ~Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)~ - [ ] ~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials~ - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] ~Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/))~ - [ ] ~Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))~ - [ ] ~If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~ - [ ] ~This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))~ - [ ] ~This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)~ ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../components/top_nav/discover_topnav.tsx | 9 ++- .../apps/discover/group2/_data_view_edit.ts | 58 ++++++++++++++++++- .../page_objects/unified_search_page.ts | 21 +++++-- 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index df7d12fc90f2f..f8aae447c5c98 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -186,10 +186,13 @@ export const DiscoverTopNav = ({ ); const onEditDataView = async (editedDataView: DataView) => { - if (!editedDataView.isPersisted()) { - await updateAdHocDataViewId(editedDataView); + if (editedDataView.isPersisted()) { + // Clear the current data view from the cache and create a new instance + // of it, ensuring we have a new object reference to trigger a re-render + dataViews.clearInstanceCache(editedDataView.id); + stateContainer.actions.setDataView(await dataViews.create(editedDataView.toSpec(), true)); } else { - stateContainer.actions.setDataView(editedDataView); + await updateAdHocDataViewId(editedDataView); } stateContainer.actions.loadDataViewList(); stateContainer.dataState.fetch(); diff --git a/test/functional/apps/discover/group2/_data_view_edit.ts b/test/functional/apps/discover/group2/_data_view_edit.ts index d1e98296b2a39..1ca3332fc1da2 100644 --- a/test/functional/apps/discover/group2/_data_view_edit.ts +++ b/test/functional/apps/discover/group2/_data_view_edit.ts @@ -45,9 +45,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { path: '/my-index-000002', method: 'DELETE', }); + await es.transport.request({ + path: '/my-index-000003', + method: 'DELETE', + }); }); - it('create data view', async function () { + it('create ad hoc data view', async function () { const initialPattern = 'my-index-'; await es.transport.request({ path: '/my-index-000001/_doc', @@ -78,10 +82,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect((await PageObjects.discover.getAllFieldNames()).length).to.be(3); }); - it('update data view', async function () { + it('create saved data view', async function () { const updatedPattern = 'my-index-000001'; await PageObjects.discover.clickIndexPatternActions(); - await PageObjects.unifiedSearch.editDataView(updatedPattern); + await PageObjects.unifiedSearch.createNewDataView(updatedPattern, false, true); await retry.try(async () => { expect(await PageObjects.discover.getHitCountInt()).to.be(1); @@ -90,5 +94,53 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSidebarHasLoaded(); expect((await PageObjects.discover.getAllFieldNames()).length).to.be(2); }); + + it('update data view with a different time field', async function () { + const updatedPattern = 'my-index-000003'; + await es.transport.request({ + path: '/my-index-000003/_doc', + method: 'POST', + body: { + timestamp: new Date('1970-01-01').toISOString(), + c: 'GET /search HTTP/1.1 200 1070000', + d: 'GET /search HTTP/1.1 200 1070000', + }, + }); + for (let i = 0; i < 3; i++) { + await es.transport.request({ + path: '/my-index-000003/_doc', + method: 'POST', + body: { + timestamp: new Date().toISOString(), + c: 'GET /search HTTP/1.1 200 1070000', + d: 'GET /search HTTP/1.1 200 1070000', + }, + }); + } + await PageObjects.discover.clickIndexPatternActions(); + await PageObjects.unifiedSearch.editDataView(updatedPattern, 'timestamp'); + await retry.try(async () => { + expect(await PageObjects.discover.getHitCountInt()).to.be(3); + }); + await PageObjects.discover.waitUntilSidebarHasLoaded(); + expect((await PageObjects.discover.getAllFieldNames()).length).to.be(3); + expect(await PageObjects.discover.isChartVisible()).to.be(true); + expect(await PageObjects.timePicker.timePickerExists()).to.be(true); + }); + + it('update data view with no time field', async function () { + await PageObjects.discover.clickIndexPatternActions(); + await PageObjects.unifiedSearch.editDataView( + undefined, + "--- I don't want to use the time filter ---" + ); + await retry.try(async () => { + expect(await PageObjects.discover.getHitCountInt()).to.be(4); + }); + await PageObjects.discover.waitUntilSidebarHasLoaded(); + expect((await PageObjects.discover.getAllFieldNames()).length).to.be(3); + expect(await PageObjects.discover.isChartVisible()).to.be(false); + expect(await PageObjects.timePicker.timePickerExists()).to.be(false); + }); }); } diff --git a/test/functional/page_objects/unified_search_page.ts b/test/functional/page_objects/unified_search_page.ts index deea5d105f5f5..cef9746a3aaff 100644 --- a/test/functional/page_objects/unified_search_page.ts +++ b/test/functional/page_objects/unified_search_page.ts @@ -12,6 +12,7 @@ export class UnifiedSearchPageObject extends FtrService { private readonly retry = this.ctx.getService('retry'); private readonly testSubjects = this.ctx.getService('testSubjects'); private readonly find = this.ctx.getService('find'); + private readonly comboBox = this.ctx.getService('comboBox'); public async switchDataView( switchButtonSelector: string, @@ -90,13 +91,21 @@ export class UnifiedSearchPageObject extends FtrService { await this.testSubjects.click(adHoc ? 'exploreIndexPatternButton' : 'saveIndexPatternButton'); } - public async editDataView(newPattern: string) { - await this.clickCreateNewDataView(); - await this.testSubjects.setValue('createIndexPatternTitleInput', newPattern, { - clearWithKeyboard: true, - typeCharByChar: true, - }); + public async editDataView(newPattern?: string, newTimeField?: string) { + await this.clickEditDataView(); + if (newPattern) { + await this.testSubjects.setValue('createIndexPatternTitleInput', newPattern, { + clearWithKeyboard: true, + typeCharByChar: true, + }); + } + if (newTimeField) { + await this.comboBox.set('timestampField', newTimeField); + } await this.testSubjects.click('saveIndexPatternButton'); + if (await this.testSubjects.exists('confirmModalConfirmButton')) { + await this.testSubjects.click('confirmModalConfirmButton'); + } } public async isAdHocDataView() { From 4e5595b92bc30bc0a7f4d7c04dd32e07ee8553bb Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 16 Feb 2023 00:54:26 -0500 Subject: [PATCH 56/68] [api-docs] 2023-02-16 Daily api_docs build (#151397) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/250 --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.devdocs.json | 14 + api_docs/alerting.mdx | 4 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 4 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_chat.mdx | 2 +- api_docs/cloud_data_migration.mdx | 2 +- api_docs/cloud_defend.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/content_management.devdocs.json | 300 +- api_docs/content_management.mdx | 4 +- api_docs/controls.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.devdocs.json | 128 - api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.devdocs.json | 80 - api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 12 +- api_docs/deprecations_by_plugin.mdx | 42 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.devdocs.json | 2 +- api_docs/files.mdx | 2 +- api_docs/files_management.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 4 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/image_embeddable.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_analytics_shippers_gainsight.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_synthtrace_client.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_cases_components.mdx | 2 +- api_docs/kbn_cell_actions.devdocs.json | 839 ++- api_docs/kbn_cell_actions.mdx | 7 +- api_docs/kbn_chart_expressions_common.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_code_editor.mdx | 2 +- api_docs/kbn_code_editor_mocks.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_content_editor.mdx | 2 +- .../kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_apps_server_internal.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 4 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 4 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser.mdx | 2 +- ..._core_custom_branding_browser_internal.mdx | 2 +- ...kbn_core_custom_branding_browser_mocks.mdx | 2 +- api_docs/kbn_core_custom_branding_common.mdx | 2 +- api_docs/kbn_core_custom_branding_server.mdx | 2 +- ...n_core_custom_branding_server_internal.mdx | 2 +- .../kbn_core_custom_branding_server_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- ...bn_core_http_resources_server_internal.mdx | 2 +- .../kbn_core_http_resources_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_server.mdx | 2 +- api_docs/kbn_core_plugins_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_rendering_server_internal.mdx | 2 +- api_docs/kbn_core_rendering_server_mocks.mdx | 2 +- api_docs/kbn_core_root_server_internal.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- ...core_saved_objects_api_server.devdocs.json | 652 +++ .../kbn_core_saved_objects_api_server.mdx | 4 +- ...core_saved_objects_api_server_internal.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ..._objects_base_server_internal.devdocs.json | 157 + ...ore_saved_objects_base_server_internal.mdx | 4 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- ...kbn_core_saved_objects_common.devdocs.json | 139 +- api_docs/kbn_core_saved_objects_common.mdx | 4 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...cts_migration_server_internal.devdocs.json | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- ...kbn_core_saved_objects_server.devdocs.json | 4791 ++++++++++++++--- api_docs/kbn_core_saved_objects_server.mdx | 10 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- ...re_saved_objects_utils_server.devdocs.json | 1540 +----- .../kbn_core_saved_objects_utils_server.mdx | 7 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_test_helpers_kbn_server.mdx | 2 +- ...n_core_test_helpers_so_type_serializer.mdx | 2 +- api_docs/kbn_core_test_helpers_test_utils.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_ecs.mdx | 2 +- api_docs/kbn_ecs_data_quality_dashboard.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_guided_onboarding.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_health_gateway_server.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_i18n_react.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_json_ast.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- .../kbn_language_documentation_popover.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_local_storage.mdx | 2 +- api_docs/kbn_ml_nested_property.mdx | 2 +- api_docs/kbn_ml_query_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_file_maps.mdx | 2 +- api_docs/kbn_repo_linter.mdx | 2 +- api_docs/kbn_repo_path.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_ecs.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- ...ared_ux_avatar_user_profile_components.mdx | 2 +- .../kbn_shared_ux_button_exit_full_screen.mdx | 2 +- ...hared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_context.mdx | 2 +- api_docs/kbn_shared_ux_file_image.mdx | 2 +- api_docs/kbn_shared_ux_file_image_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_picker.mdx | 2 +- api_docs/kbn_shared_ux_file_upload.mdx | 2 +- api_docs/kbn_shared_ux_file_util.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_markdown.mdx | 2 +- api_docs/kbn_shared_ux_markdown_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_prompt_not_found.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.devdocs.json | 84 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_ts_projects.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_actions_browser.mdx | 2 +- api_docs/kbn_ui_shared_deps_src.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.devdocs.json | 12 + api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.devdocs.json | 14 + api_docs/lens.mdx | 4 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.devdocs.json | 36 +- api_docs/observability.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 36 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.devdocs.json | 126 +- api_docs/spaces.mdx | 4 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 493 files changed, 6710 insertions(+), 3282 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index e4c065f9b9ebf..01796d9d6e216 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index d58d467db73ee..6660f43f9fc04 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index c156023f94dbf..459b12ad51964 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index 883c6986fb91b..772cc263ceccf 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -4248,6 +4248,20 @@ "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.ActionVariable.usesPublicBaseUrl", + "type": "CompoundType", + "tags": [], + "label": "usesPublicBaseUrl", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/alerting/common/rule.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 75ba3e3ba567e..045fc7332d186 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 488 | 1 | 477 | 39 | +| 489 | 1 | 478 | 39 | ## Client diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index d800d325dc289..b874620614d43 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 86ba870d358f9..1e0d6b35b0cab 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; -Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) for questions regarding this plugin. +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 0cf8ad136cb70..2cf6be5ee966b 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index a5699f4db79a7..a5774ff145599 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 543df114bbba6..864b7b1fcbc37 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 4e3ef56f79ad4..bda164b248d35 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index a90a158fdc200..228b64499a587 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index e0409288b5b7f..1585c8444b6f0 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 5243f3f348c74..fc56cff42156b 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index b49a4e1e92bf4..1dcbe975bc599 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 342526c66bfbc..daadccc54a779 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 132d880eb47db..bb9678e4df83a 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 2f336e840c10c..54db6d38fa9b3 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.devdocs.json b/api_docs/content_management.devdocs.json index cd44c8835e205..e07703f73f764 100644 --- a/api_docs/content_management.devdocs.json +++ b/api_docs/content_management.devdocs.json @@ -194,6 +194,72 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "contentManagement", + "id": "def-common.DeleteIn", + "type": "Interface", + "tags": [], + "label": "DeleteIn", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "common", + "docId": "kibContentManagementPluginApi", + "section": "def-common.DeleteIn", + "text": "DeleteIn" + }, + "" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-common.DeleteIn.contentType", + "type": "Uncategorized", + "tags": [], + "label": "contentType", + "description": [], + "signature": [ + "T" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.DeleteIn.data", + "type": "Uncategorized", + "tags": [], + "label": "data", + "description": [], + "signature": [ + "Data" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.DeleteIn.options", + "type": "Uncategorized", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "Options | undefined" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "contentManagement", "id": "def-common.GetIn", @@ -311,6 +377,176 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.SearchIn", + "type": "Interface", + "tags": [], + "label": "SearchIn", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "common", + "docId": "kibContentManagementPluginApi", + "section": "def-common.SearchIn", + "text": "SearchIn" + }, + "" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-common.SearchIn.contentType", + "type": "Uncategorized", + "tags": [], + "label": "contentType", + "description": [], + "signature": [ + "T" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.SearchIn.params", + "type": "Uncategorized", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "Params" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.SearchIn.options", + "type": "Uncategorized", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "Options | undefined" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.SearchOut", + "type": "Interface", + "tags": [], + "label": "SearchOut", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "common", + "docId": "kibContentManagementPluginApi", + "section": "def-common.SearchOut", + "text": "SearchOut" + }, + "" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-common.SearchOut.hits", + "type": "Array", + "tags": [], + "label": "hits", + "description": [], + "signature": [ + "Data[]" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.UpdateIn", + "type": "Interface", + "tags": [], + "label": "UpdateIn", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "common", + "docId": "kibContentManagementPluginApi", + "section": "def-common.UpdateIn", + "text": "UpdateIn" + }, + "" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-common.UpdateIn.contentType", + "type": "Uncategorized", + "tags": [], + "label": "contentType", + "description": [], + "signature": [ + "T" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.UpdateIn.data", + "type": "Uncategorized", + "tags": [], + "label": "data", + "description": [], + "signature": [ + "Data" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.UpdateIn.options", + "type": "Uncategorized", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "Options | undefined" + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ], "enums": [], @@ -353,7 +589,7 @@ "label": "ProcedureName", "description": [], "signature": [ - "\"create\" | \"get\"" + "\"create\" | \"update\" | \"get\" | \"delete\" | \"search\"" ], "path": "src/plugins/content_management/common/rpc.ts", "deprecated": false, @@ -370,7 +606,7 @@ "label": "procedureNames", "description": [], "signature": [ - "readonly [\"get\", \"create\"]" + "readonly [\"get\", \"create\", \"update\", \"delete\", \"search\"]" ], "path": "src/plugins/content_management/common/rpc.ts", "deprecated": false, @@ -427,6 +663,66 @@ "path": "src/plugins/content_management/common/rpc.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.schemas.update", + "type": "Object", + "tags": [], + "label": "update", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "common", + "docId": "kibContentManagementPluginApi", + "section": "def-common.ProcedureSchemas", + "text": "ProcedureSchemas" + } + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.schemas.delete", + "type": "Object", + "tags": [], + "label": "delete", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "common", + "docId": "kibContentManagementPluginApi", + "section": "def-common.ProcedureSchemas", + "text": "ProcedureSchemas" + } + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-common.schemas.search", + "type": "Object", + "tags": [], + "label": "search", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "common", + "docId": "kibContentManagementPluginApi", + "section": "def-common.ProcedureSchemas", + "text": "ProcedureSchemas" + } + ], + "path": "src/plugins/content_management/common/rpc.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index bc7b74169bd1a..ac8dc97d4332b 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 25 | 0 | 25 | 3 | +| 42 | 0 | 42 | 3 | ## Client diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 04afbd13e3b6c..c5d69a448ec3b 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 5a50caf5c44e3..28bf4b9cf34e0 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index f931d509ae5a5..a1cf418f9a407 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index c749bf94bb3e6..27a7bee70ea33 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index e8037ea98316b..616abe16084f5 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -10545,38 +10545,6 @@ "plugin": "@kbn/core-saved-objects-api-browser", "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, { "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" @@ -10593,18 +10561,6 @@ "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, { "plugin": "@kbn/core-saved-objects-browser-internal", "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts" @@ -10941,18 +10897,6 @@ "plugin": "@kbn/core-saved-objects-browser-mocks", "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts" }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, { "plugin": "@kbn/core-saved-objects-import-export-server-internal", "path": "packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/errors.ts" @@ -14029,14 +13973,6 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/logs/settings/validation_errors.ts" }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx" - }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx" - }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx" @@ -21660,14 +21596,6 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/logs/settings/validation_errors.ts" }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx" - }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx" - }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx" @@ -28414,38 +28342,6 @@ "plugin": "@kbn/core-saved-objects-api-browser", "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, { "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" @@ -28462,18 +28358,6 @@ "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, { "plugin": "@kbn/core-saved-objects-browser-internal", "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts" @@ -28810,18 +28694,6 @@ "plugin": "@kbn/core-saved-objects-browser-mocks", "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts" }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, { "plugin": "@kbn/core-saved-objects-import-export-server-internal", "path": "packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/errors.ts" diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 321f95671418d..6130c36147573 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 95e89129ec02f..75a57004aebde 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 632a60e30f6b0..0adfe5be98204 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 70d7ffddd3609..db68fbd49a257 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 216c74ebb9eec..f587f3f68875f 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 3b44ec9249ac4..2f3dc1c92f60d 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index 108a6ba16bccc..c16c65d2dc322 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -499,14 +499,6 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/logs/settings/validation_errors.ts" }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx" - }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx" - }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx" @@ -8715,14 +8707,6 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/logs/settings/validation_errors.ts" }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx" - }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx" - }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx" @@ -16026,14 +16010,6 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/logs/settings/validation_errors.ts" }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx" - }, - { - "plugin": "fleet", - "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx" - }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx" @@ -25819,38 +25795,6 @@ "plugin": "@kbn/core-saved-objects-api-browser", "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, { "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" @@ -25867,18 +25811,6 @@ "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, { "plugin": "@kbn/core-saved-objects-browser-internal", "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts" @@ -26159,18 +26091,6 @@ "plugin": "@kbn/core-saved-objects-browser-mocks", "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts" }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, { "plugin": "@kbn/core-saved-objects-import-export-server-internal", "path": "packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/errors.ts" diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 1467b9fdbb28b..938ca42f41987 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index af1140a0c0fa8..fc85563f7f310 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index fa95fc518c039..7e487d52132f8 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,10 +21,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | alerting, discover, securitySolution | - | | | stackAlerts, alerting, securitySolution, inputControlVis | - | | | actions, alerting | - | +| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-server, @kbn/core, actions, alerting, canvas, enterpriseSearch, securitySolution, taskManager, dashboard, savedSearch, @kbn/core-saved-objects-server-internal, @kbn/core-saved-objects-api-server, savedObjects, embeddable, visualizations, fleet, infra, graph, ml | - | | | @kbn/core-saved-objects-migration-server-internal, actions, dataViews, data, alerting, savedObjectsTagging, canvas, lens, cases, graph, lists, maps, securitySolution, dashboard, savedSearch, visualizations, @kbn/core-test-helpers-so-type-serializer | - | -| | @kbn/core-saved-objects-common, @kbn/core, actions, alerting, canvas, enterpriseSearch, securitySolution, taskManager, dashboard, savedSearch, @kbn/core-saved-objects-server-internal, savedObjects, embeddable, visualizations, fleet, infra, graph, ml | - | -| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, discover, savedObjectsTagging, savedObjectsFinder, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-server, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, dashboard | - | -| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, discover, savedObjectsTagging, savedObjectsFinder, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-server, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, data | - | +| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, discover, savedObjectsTagging, savedObjectsFinder, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, dashboard | - | +| | @kbn/core-saved-objects-common, @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-api-server, @kbn/core, home, dataViews, discover, savedObjectsTagging, savedObjectsFinder, fleet, canvas, osquery, securitySolution, synthetics, savedObjects, @kbn/core-saved-objects-browser-mocks, @kbn/core-saved-objects-import-export-server-internal, apm, savedObjectsTaggingOss, cases, lists, upgradeAssistant, savedObjectsManagement, @kbn/core-ui-settings-server-internal, data | - | | | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | | | discover | - | | | @kbn/es-query, securitySolution, timelines, lists, threatIntelligence, dataViews, unifiedSearch, triggersActionsUi, savedObjectsManagement, controls, unifiedFieldList, lens, aiops, ml, infra, visTypeTimeseries, apm, observability, dataVisualizer, fleet, canvas, graph, stackAlerts, synthetics, transform, upgradeAssistant, ux, maps, dataViewManagement, inputControlVis, visDefaultEditor, presentationUtil, visTypeTimelion, visTypeVega, discover, data | - | @@ -53,7 +53,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | dashboard, dataVisualizer, stackAlerts, expressionPartitionVis | - | | | monitoring | - | | | @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, visualizations, dashboard, ml, infra, cloudSecurityPosture, dashboardEnhanced, monitoring, synthetics, @kbn/core-saved-objects-browser-internal | - | -| | @kbn/core-saved-objects-api-browser, @kbn/core-saved-objects-api-server, @kbn/core, savedObjects, savedObjectsManagement, visualizations, savedObjectsTagging, lens, fleet, graph, dashboard, savedObjectsTaggingOss, kibanaUtils, expressions, dataViews, data, embeddable, controls, uiActionsEnhanced, maps, canvas, cases, dashboardEnhanced, globalSearchProviders, infra | - | +| | @kbn/core-saved-objects-api-browser, @kbn/core, savedObjects, savedObjectsManagement, visualizations, savedObjectsTagging, lens, fleet, graph, dashboard, savedObjectsTaggingOss, kibanaUtils, expressions, dataViews, data, embeddable, controls, uiActionsEnhanced, maps, canvas, cases, dashboardEnhanced, globalSearchProviders, infra | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, savedSearch, visualizations, dashboard, lens, maps, infra, graph, synthetics | - | | | @kbn/core-saved-objects-browser-mocks, home, @kbn/core-saved-objects-browser-internal | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core-saved-objects-browser-mocks, dataViews, savedObjects, visualizations, dashboard, maps, infra, graph | - | @@ -84,7 +84,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/core | - | | | @kbn/core, advancedSettings, triggersActionsUi, visualizations | - | | | home, canvas, osquery | - | -| | home, data, esUiShared, spaces, savedObjectsManagement, fleet, observability, ml, apm, indexLifecycleManagement, synthetics, upgradeAssistant, ux, kibanaOverview | - | +| | home, data, esUiShared, spaces, savedObjectsManagement, fleet, observability, ml, apm, enterpriseSearch, indexLifecycleManagement, synthetics, upgradeAssistant, ux, kibanaOverview | - | | | dataViews, maps | - | | | dataViewManagement, dataViews | - | | | visTypeTimeseries, graph, dataViewManagement, dataViews | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index acc880ee82ea5..ec2e386d2715a 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -34,12 +34,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectsBulkUpdateOptions) | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectsBulkResolveResponse) | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectsBulkCreateObject) | - | +| | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/server/index.ts#:~:text=SavedObjectAttributes), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectAttributes), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/types/index.ts#:~:text=SavedObjectAttributes), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/server/index.ts#:~:text=SavedObjectAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectsStart), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/server/index.ts#:~:text=SavedObjectsStart) | - | | | [mocks.ts](https://github.com/elastic/kibana/tree/main/src/core/public/mocks.ts#:~:text=savedObjectsServiceMock) | - | | | [mocks.ts](https://github.com/elastic/kibana/tree/main/src/core/public/mocks.ts#:~:text=simpleSavedObjectMock) | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectAttributeSingle), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/types/index.ts#:~:text=SavedObjectAttributeSingle) | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectAttribute), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/types/index.ts#:~:text=SavedObjectAttribute) | - | -| | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectAttributes), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/types/index.ts#:~:text=SavedObjectAttributes), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/server/index.ts#:~:text=SavedObjectAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/src/core/public/index.ts#:~:text=SavedObjectReference), [index.ts](https://github.com/elastic/kibana/tree/main/src/core/types/index.ts#:~:text=SavedObjectReference) | - | @@ -126,9 +126,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [base.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts#:~:text=SavedObject), [base.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts#:~:text=SavedObject), [resolve.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts#:~:text=SavedObject), [resolve.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts#:~:text=SavedObject), [update.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts#:~:text=SavedObject), [update.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject)+ 5 more | - | -| | [base.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts#:~:text=SavedObject), [base.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts#:~:text=SavedObject), [resolve.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts#:~:text=SavedObject), [resolve.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts#:~:text=SavedObject), [update.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts#:~:text=SavedObject), [update.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts#:~:text=SavedObject), [find.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject)+ 35 more | - | -| | [bulk_create.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts#:~:text=SavedObjectReference), [bulk_create.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts#:~:text=SavedObjectReference), [update.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts#:~:text=SavedObjectReference), [update.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts#:~:text=SavedObjectReference), [update.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts#:~:text=SavedObjectReference), [create.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts#:~:text=SavedObjectReference), [create.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts#:~:text=SavedObjectReference) | - | +| | [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject) | - | +| | [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject), [saved_objects_repository.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts#:~:text=SavedObject)+ 2 more | - | +| | [index.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server/index.ts#:~:text=SavedObjectAttributes) | - | @@ -206,7 +206,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | [index.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/index.ts#:~:text=SavedObject) | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/index.ts#:~:text=SavedObject), [index.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/index.ts#:~:text=SavedObject) | - | -| | [server_types.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts#:~:text=SavedObjectAttributes), [server_types.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts#:~:text=SavedObjectAttributes), [saved_objects.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/saved_objects.ts#:~:text=SavedObjectAttributes) | - | +| | [server_types.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts#:~:text=SavedObjectAttributes), [server_types.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts#:~:text=SavedObjectAttributes), [saved_objects.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/saved_objects.ts#:~:text=SavedObjectAttributes), [server_types.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts#:~:text=SavedObjectAttributes), [server_types.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/server_types.ts#:~:text=SavedObjectAttributes), [saved_objects.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-common/src/saved_objects.ts#:~:text=SavedObjectAttributes) | - | @@ -223,7 +223,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [utils.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/utils.ts#:~:text=convertToMultiNamespaceTypeVersion), [internal_transforms.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/internal_transforms.ts#:~:text=convertToMultiNamespaceTypeVersion), [internal_transforms.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/internal_transforms.ts#:~:text=convertToMultiNamespaceTypeVersion), [internal_transforms.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/internal_transforms.ts#:~:text=convertToMultiNamespaceTypeVersion), [validate_migrations.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion)+ 14 more | - | +| | [utils.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/utils.ts#:~:text=convertToMultiNamespaceTypeVersion), [internal_transforms.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/internal_transforms.ts#:~:text=convertToMultiNamespaceTypeVersion), [internal_transforms.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/internal_transforms.ts#:~:text=convertToMultiNamespaceTypeVersion), [internal_transforms.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/internal_transforms.ts#:~:text=convertToMultiNamespaceTypeVersion), [validate_migrations.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts#:~:text=convertToMultiNamespaceTypeVersion), [validate_migrations.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion), [document_migrator.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts#:~:text=convertToMultiNamespaceTypeVersion)+ 18 more | - | @@ -231,8 +231,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject) | - | -| | [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject), [security.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts#:~:text=SavedObject) | - | +| | [index.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server/index.ts#:~:text=SavedObjectAttributes) | - | @@ -240,7 +239,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [collect_references_deep.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts#:~:text=SavedObjectAttributes), [collect_references_deep.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts#:~:text=SavedObjectAttributes) | - | +| | [collect_references_deep.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts#:~:text=SavedObjectAttributes), [collect_references_deep.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts#:~:text=SavedObjectAttributes), [collect_references_deep.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts#:~:text=SavedObjectAttributes), [collect_references_deep.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts#:~:text=SavedObjectAttributes) | - | @@ -289,8 +288,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/plugin.ts#:~:text=authc) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/plugin.ts#:~:text=authz) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/plugin.ts#:~:text=index), [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/plugin.ts#:~:text=index) | - | +| | [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/types.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/types.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/types.ts#:~:text=SavedObjectAttributes)+ 16 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/saved_objects/index.ts#:~:text=convertToMultiNamespaceTypeVersion), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/saved_objects/index.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | -| | [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [actions_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/actions_client.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/types.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/types.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/actions/server/types.ts#:~:text=SavedObjectAttributes)+ 3 more | - | @@ -325,8 +324,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [plugin.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/plugin.test.ts#:~:text=getKibanaFeatures) | 8.8.0 | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/plugin.ts#:~:text=license%24), [license_state.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/lib/license_state.test.ts#:~:text=license%24), [license_state.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/lib/license_state.test.ts#:~:text=license%24) | 8.8.0 | | | [task.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/usage/task.ts#:~:text=index) | - | +| | [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts#:~:text=SavedObjectAttributes), [migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts#:~:text=SavedObjectAttributes)+ 38 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/saved_objects/index.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | -| | [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/common/rule.ts#:~:text=SavedObjectAttributes), [migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts#:~:text=SavedObjectAttributes), [migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts#:~:text=SavedObjectAttributes)+ 14 more | - | @@ -370,8 +369,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [platform.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/kibana/platform.ts#:~:text=savedObjects), [platform.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/kibana/platform.ts#:~:text=savedObjects) | - | | | [platform.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/platform.ts#:~:text=SavedObjectsClientContract), [platform.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/platform.ts#:~:text=SavedObjectsClientContract) | - | | | [workpad.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/workpad.ts#:~:text=ResolvedSimpleSavedObject), [workpad.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/workpad.ts#:~:text=ResolvedSimpleSavedObject), [workpad.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/workpad.ts#:~:text=ResolvedSimpleSavedObject), [workpad.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/workpad.ts#:~:text=ResolvedSimpleSavedObject) | - | +| | [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/custom_elements/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/custom_elements/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/workpad/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/workpad/find.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/shareable_runtime/types.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/shareable_runtime/types.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/custom_elements/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/custom_elements/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/workpad/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/workpad/find.ts#:~:text=SavedObjectAttributes) | - | | | [platform.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/platform.ts#:~:text=SavedObjectsStart), [platform.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/public/services/platform.ts#:~:text=SavedObjectsStart) | - | -| | [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/shareable_runtime/types.ts#:~:text=SavedObjectAttributes), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/shareable_runtime/types.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/custom_elements/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/custom_elements/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/workpad/find.ts#:~:text=SavedObjectAttributes), [find.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/routes/workpad/find.ts#:~:text=SavedObjectAttributes) | - | | | [saved_lens.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts#:~:text=SavedObjectReference), [saved_lens.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts#:~:text=SavedObjectReference), [saved_map.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts#:~:text=SavedObjectReference), [saved_map.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts#:~:text=SavedObjectReference), [saved_search.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.ts#:~:text=SavedObjectReference), [saved_search.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.ts#:~:text=SavedObjectReference), [saved_visualization.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts#:~:text=SavedObjectReference), [saved_visualization.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts#:~:text=SavedObjectReference), [embeddable.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=SavedObjectReference), [embeddable.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts#:~:text=SavedObjectReference) | - | | | [workpad.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/saved_objects/workpad.ts#:~:text=convertToMultiNamespaceTypeVersion), [custom_element.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/canvas/server/saved_objects/custom_element.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | @@ -457,9 +456,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=resolve) | - | | | [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SimpleSavedObject), [find_dashboard_saved_objects.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/find_dashboard_saved_objects.ts#:~:text=SimpleSavedObject), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx#:~:text=SimpleSavedObject), [dashboard_listing.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_app/listing/dashboard_listing.tsx#:~:text=SimpleSavedObject) | - | | | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=ResolvedSimpleSavedObject), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=ResolvedSimpleSavedObject) | - | +| | [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [migrate_extract_panel_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry_collection_task.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [dashboard_telemetry.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/dashboard_telemetry.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [find_by_value_embeddables.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/usage/find_by_value_embeddables.ts#:~:text=SavedObjectAttributes), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes)+ 20 more | - | | | [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx#:~:text=SavedObjectsStart), [clone_panel_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/dashboard_actions/clone_panel_action.tsx#:~:text=SavedObjectsStart) | - | | | [dashboard_saved_object.stub.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts#:~:text=savedObjectsServiceMock), [dashboard_saved_object.stub.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/dashboard_saved_object.stub.ts#:~:text=savedObjectsServiceMock) | - | -| | [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes), [load_dashboard_state_from_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/public/services/dashboard_saved_object/lib/load_dashboard_state_from_saved_object.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectAttributes)+ 11 more | - | | | [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/bwc/types.ts#:~:text=SavedObjectReference), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/bwc/types.ts#:~:text=SavedObjectReference), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectReference), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectReference), [dashboard_saved_object_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_saved_object/persistable_state/dashboard_saved_object_references.ts#:~:text=SavedObjectReference), [dashboard_container_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_container/persistable_state/dashboard_container_references.ts#:~:text=SavedObjectReference), [dashboard_container_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_container/persistable_state/dashboard_container_references.ts#:~:text=SavedObjectReference), [dashboard_container_references.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/common/dashboard_container/persistable_state/dashboard_container_references.ts#:~:text=SavedObjectReference) | - | | | [dashboard_saved_object.ts](https://github.com/elastic/kibana/tree/main/src/plugins/dashboard/server/dashboard_saved_object/dashboard_saved_object.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | @@ -613,9 +612,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| +| | [analytics_collection_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx#:~:text=RedirectAppLinks), [analytics_collection_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx#:~:text=RedirectAppLinks), [analytics_collection_view.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx#:~:text=RedirectAppLinks) | - | | | [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi), [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi), [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi), [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi) | - | | | [check_access.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz), [check_access.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz), [check_access.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz) | - | -| | [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes) | - | +| | [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes) | - | @@ -657,9 +657,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject) | - | | | [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject), [epm.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/common/types/models/epm.ts#:~:text=SavedObject) | - | -| | [filter_dataset.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx#:~:text=title), [filter_log_level.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx#:~:text=title), [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title), [filter_dataset.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx#:~:text=title), [filter_log_level.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx#:~:text=title), [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title) | - | -| | [filter_dataset.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx#:~:text=title), [filter_log_level.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx#:~:text=title), [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title), [filter_dataset.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx#:~:text=title), [filter_log_level.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx#:~:text=title), [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title) | - | -| | [filter_dataset.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx#:~:text=title), [filter_log_level.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx#:~:text=title), [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title) | - | +| | [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title), [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title) | - | +| | [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title), [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title) | - | +| | [query_bar.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/query_bar.tsx#:~:text=title) | - | | | [use_get_logs_discover_link.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/hooks/use_get_logs_discover_link.tsx#:~:text=indexPatternId) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/server/plugin.ts#:~:text=disabled) | 8.8.0 | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/fleet/server/plugin.ts#:~:text=disabled) | 8.8.0 | @@ -1067,10 +1067,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [get_saved_searches.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.ts#:~:text=resolve), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=resolve), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=resolve), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=resolve), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=resolve), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=resolve), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=resolve) | - | | | [save_saved_searches.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.ts#:~:text=update), [save_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts#:~:text=update), [save_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts#:~:text=update) | - | | | [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/types.ts#:~:text=ResolvedSimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/types.ts#:~:text=ResolvedSimpleSavedObject) | - | +| | [search_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/server/saved_objects/search_migrations.ts#:~:text=SavedObjectAttributes), [search_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/server/saved_objects/search_migrations.ts#:~:text=SavedObjectAttributes), [search_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/server/saved_objects/search_migrations.ts#:~:text=SavedObjectAttributes), [search_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/server/saved_objects/search_migrations.ts#:~:text=SavedObjectAttributes) | - | | | [save_saved_searches.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.ts#:~:text=SavedObjectsStart), [save_saved_searches.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.ts#:~:text=SavedObjectsStart), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=SavedObjectsStart), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=SavedObjectsStart), [save_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts#:~:text=SavedObjectsStart), [save_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts#:~:text=SavedObjectsStart) | - | | | [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=savedObjectsServiceMock), [get_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts#:~:text=savedObjectsServiceMock), [save_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts#:~:text=savedObjectsServiceMock), [save_saved_searches.test.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts#:~:text=savedObjectsServiceMock) | - | | | [search.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/server/saved_objects/search.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | -| | [search_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/server/saved_objects/search_migrations.ts#:~:text=SavedObjectAttributes), [search_migrations.ts](https://github.com/elastic/kibana/tree/main/src/plugins/saved_search/server/saved_objects/search_migrations.ts#:~:text=SavedObjectAttributes) | - | @@ -1131,8 +1131,8 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObjectsClientContract), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObjectsClientContract), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=SavedObjectsClientContract), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.test.ts#:~:text=SavedObjectsClientContract), [utils.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.test.ts#:~:text=SavedObjectsClientContract) | - | | | [use_dashboard_button_href.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/hooks/use_dashboard_button_href.ts#:~:text=find), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx#:~:text=find), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx#:~:text=find), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=find), [utils.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/containers/dashboards/utils.ts#:~:text=find) | - | | | [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/hooks/types.ts#:~:text=SimpleSavedObject), [types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/common/hooks/types.ts#:~:text=SimpleSavedObject) | - | +| | [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes) | - | | | [timelines.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts#:~:text=convertToMultiNamespaceTypeVersion), [notes.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts#:~:text=convertToMultiNamespaceTypeVersion), [pinned_events.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts#:~:text=convertToMultiNamespaceTypeVersion), [legacy_saved_object_mappings.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_saved_object_mappings.ts#:~:text=convertToMultiNamespaceTypeVersion) | - | -| | [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_types.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes), [legacy_migrations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts#:~:text=SavedObjectAttributes) | - | | | [policy_hooks.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [policy_hooks.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [api_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/api_client.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [api_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/api_client.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [api_client.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/api_client.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [lists.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID), [manifest_manager.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_ID)+ 34 more | - | | | [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_NAME) | - | | | [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts#:~:text=ENDPOINT_TRUSTED_APPS_LIST_DESCRIPTION) | - | @@ -1208,7 +1208,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [task_store.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/task_manager/server/task_store.test.ts#:~:text=SavedObjectAttributes), [task_store.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/task_manager/server/task_store.test.ts#:~:text=SavedObjectAttributes) | - | +| | [task_store.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/task_manager/server/task_store.test.ts#:~:text=SavedObjectAttributes), [task_store.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/task_manager/server/task_store.test.ts#:~:text=SavedObjectAttributes), [task_store.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/task_manager/server/task_store.test.ts#:~:text=SavedObjectAttributes), [task_store.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/task_manager/server/task_store.test.ts#:~:text=SavedObjectAttributes) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index dac3f91af8901..1c7ec65271f44 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 3361a8b6afa62..19c685f5d07f5 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 96fa5b0db7b6b..8a6210844db85 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 19f537c7686b0..b4551b83e1726 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 49228282fa1df..53b17c01735ee 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 9cd098cec4943..2912d2b598553 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 85059424cefac..82c1486689a80 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index c9ba2ae55493c..561e5511d4f8a 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 5f8d386710d5a..5d73393736696 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index aef907e9daf5e..a11bb262d597b 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 9a4346f628c73..307341e1df79e 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index fd0dda38e0606..f3b90aefa2917 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 482ac924c3343..125882023b0c2 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 960185c05fd14..3d1e900efdc63 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 942fa6e336be5..8317a90f00b9c 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 1563f0daa66c7..84335fca321f1 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index afc68bf55fe06..7d2a39d25068c 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index bb5d3f5f82ab3..5f56c15876bf5 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 4182ea1f5ca05..be0ea397efef9 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index a04c44eae02d0..8c3f99da4e44f 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 293e82177cbea..231d4bfd3ee60 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index a6bdd2b117a36..bde358a7e3d11 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index f3218767c7d33..a3137a10e5a54 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index cec06d5c45b0f..fdb7580f7242a 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 07bde0d9dff2c..06ca3e0a50bed 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 060933ccb4b96..facc3ced12015 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 80d9d3bfe524b..7a7b44331611f 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 3aaea60931ab8..7d81280fae05d 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 323aab019c973..11ba5bd516c9c 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index 53059366ece6c..462d19625454f 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -5350,7 +5350,7 @@ "\nSpecify which HTTP routes to create for the file kind.\n\nYou can always create your own HTTP routes for working with files but\nthis interface allows you to expose basic CRUD operations, upload, download\nand sharing of files over a RESTful-like interface.\n" ], "signature": [ - "{ create?: any; update?: any; delete?: any; getById?: any; list?: any; download?: any; share?: any; }" + "{ create?: HttpEndpointDefinition | undefined; update?: HttpEndpointDefinition | undefined; delete?: HttpEndpointDefinition | undefined; getById?: HttpEndpointDefinition | undefined; list?: HttpEndpointDefinition | undefined; download?: HttpEndpointDefinition | undefined; share?: HttpEndpointDefinition | undefined; }" ], "path": "packages/shared-ux/file/types/index.d.ts", "deprecated": false, diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 28534c867291a..66f541c80817c 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 29b1cc24ad8e4..8df86ca78898f 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 7cf96a2b9254b..104b2550f6c8d 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 5bbdc632924b4..937f7ac4b119f 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; -Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) for questions regarding this plugin. +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index d7ff415f1ad20..4f4fe8e1456a5 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 407ac6d523aa4..474bcb99cb062 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 5e14a770666d9..53b82662e70ac 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 74f900705b85c..173f639693ca2 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 6ee92daa82fc0..4f8d00209d4c0 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index d4d5730a704ee..cf1fdb6cd1dbf 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 3d12adcaf6c1a..51e4f190a255e 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 6e321dd674856..e0fff424e74a4 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 2ed388278c263..ca1b4c40d2f7f 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index a6d59442a2ae6..1606fb0e50684 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index f0f4bd78b2701..6fba76a809cd2 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index cd6551dc99755..82f35c41759dc 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 65c3fbfd7e4e8..5fdb1e6590ba6 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 95dd4c03fb08f..f35a33e3b53ca 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 2d5c5a32746f6..59fd65a0247f4 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 1230f393d9699..cfa2f738df491 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index e342240f26e97..d82f788dae55d 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 6689efbd71033..4712186a07c5a 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 06e914b7a0a47..8ffb3c3bc5fe1 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index b9e015ca242b8..3e438262ecca9 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index a76b3ac5f7196..18a1976224995 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 5a9885f4fcd88..227c058ff12ee 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index a921994216927..b4d728183292c 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index fe42ac3d4d642..779dc409d9d70 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 330f1e2cb0fa7..f475efa099218 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index e131ea5e8c412..041a9e55d93cf 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.devdocs.json b/api_docs/kbn_cell_actions.devdocs.json index 4d6a0fd76d2d5..e24dccb321638 100644 --- a/api_docs/kbn_cell_actions.devdocs.json +++ b/api_docs/kbn_cell_actions.devdocs.json @@ -19,6 +19,176 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterIn", + "type": "Function", + "tags": [], + "label": "addFilterIn", + "description": [], + "signature": [ + "({ filterManager, fieldName, value, }: { filterManager: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + " | undefined; fieldName: string; value: string | string[] | null | undefined; }) => void" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterIn.$1", + "type": "Object", + "tags": [], + "label": "{\n filterManager,\n fieldName,\n value,\n}", + "description": [], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterIn.$1.filterManager", + "type": "Object", + "tags": [], + "label": "filterManager", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + " | undefined" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterIn.$1.fieldName", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterIn.$1.value", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "string | string[] | null | undefined" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterOut", + "type": "Function", + "tags": [], + "label": "addFilterOut", + "description": [], + "signature": [ + "({ filterManager, fieldName, value, }: { filterManager: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + " | undefined; fieldName: string; value: string | string[] | null | undefined; }) => void" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterOut.$1", + "type": "Object", + "tags": [], + "label": "{\n filterManager,\n fieldName,\n value,\n}", + "description": [], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterOut.$1.filterManager", + "type": "Object", + "tags": [], + "label": "filterManager", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + " | undefined" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterOut.$1.fieldName", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.addFilterOut.$1.value", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "string | string[] | null | undefined" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/cell-actions", "id": "def-common.CellActions", @@ -27,8 +197,14 @@ "label": "CellActions", "description": [], "signature": [ - "({ field, triggerId, children, mode, showActionTooltips, visibleCellActions, disabledActions, metadata, className, }: React.PropsWithChildren<", - "CellActionsProps", + "({ field, triggerId, children, mode, showActionTooltips, visibleCellActions, disabledActionTypes, metadata, className, }: React.PropsWithChildren<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionsProps", + "text": "CellActionsProps" + }, ">) => JSX.Element" ], "path": "packages/kbn-cell-actions/src/components/cell_actions.tsx", @@ -40,11 +216,17 @@ "id": "def-common.CellActions.$1", "type": "CompoundType", "tags": [], - "label": "{\n field,\n triggerId,\n children,\n mode,\n showActionTooltips = true,\n visibleCellActions = 3,\n disabledActions = [],\n metadata,\n className,\n}", + "label": "{\n field,\n triggerId,\n children,\n mode,\n showActionTooltips = true,\n visibleCellActions = 3,\n disabledActionTypes = [],\n metadata,\n className,\n}", "description": [], "signature": [ "React.PropsWithChildren<", - "CellActionsProps", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionsProps", + "text": "CellActionsProps" + }, ">" ], "path": "packages/kbn-cell-actions/src/components/cell_actions.tsx", @@ -93,6 +275,287 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createCellActionFactory", + "type": "Function", + "tags": [], + "label": "createCellActionFactory", + "description": [], + "signature": [ + " = ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellAction", + "text": "CellAction" + }, + "<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionExecutionContext", + "text": "CellActionExecutionContext" + }, + ">, P = void>(actionCreator: (params: P) => ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionTemplate", + "text": "CellActionTemplate" + }, + ") => (params: P) => ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionFactory", + "text": "CellActionFactory" + }, + "" + ], + "path": "packages/kbn-cell-actions/src/actions/factory.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createCellActionFactory.$1", + "type": "Function", + "tags": [], + "label": "actionCreator", + "description": [], + "signature": [ + "(params: P) => ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionTemplate", + "text": "CellActionTemplate" + }, + "" + ], + "path": "packages/kbn-cell-actions/src/actions/factory.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createCopyToClipboardActionFactory", + "type": "Function", + "tags": [], + "label": "createCopyToClipboardActionFactory", + "description": [], + "signature": [ + "(params: { notifications: ", + { + "pluginId": "@kbn/core-notifications-browser", + "scope": "common", + "docId": "kibKbnCoreNotificationsBrowserPluginApi", + "section": "def-common.NotificationsStart", + "text": "NotificationsStart" + }, + "; }) => ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionFactory", + "text": "CellActionFactory" + }, + "<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellAction", + "text": "CellAction" + }, + "<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionExecutionContext", + "text": "CellActionExecutionContext" + }, + ">>" + ], + "path": "packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createCopyToClipboardActionFactory.$1", + "type": "Uncategorized", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "P" + ], + "path": "packages/kbn-cell-actions/src/actions/factory.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createFilterInActionFactory", + "type": "Function", + "tags": [], + "label": "createFilterInActionFactory", + "description": [], + "signature": [ + "(params: { filterManager: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + "; }) => ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionFactory", + "text": "CellActionFactory" + }, + "<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellAction", + "text": "CellAction" + }, + "<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionExecutionContext", + "text": "CellActionExecutionContext" + }, + ">>" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createFilterInActionFactory.$1", + "type": "Uncategorized", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "P" + ], + "path": "packages/kbn-cell-actions/src/actions/factory.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createFilterOutActionFactory", + "type": "Function", + "tags": [], + "label": "createFilterOutActionFactory", + "description": [], + "signature": [ + "(params: { filterManager: ", + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataQueryPluginApi", + "section": "def-public.FilterManager", + "text": "FilterManager" + }, + "; }) => ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionFactory", + "text": "CellActionFactory" + }, + "<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellAction", + "text": "CellAction" + }, + "<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionExecutionContext", + "text": "CellActionExecutionContext" + }, + ">>" + ], + "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.createFilterOutActionFactory.$1", + "type": "Uncategorized", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "P" + ], + "path": "packages/kbn-cell-actions/src/actions/factory.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/cell-actions", "id": "def-common.useDataGridColumnsCellActions", @@ -101,7 +564,7 @@ "label": "useDataGridColumnsCellActions", "description": [], "signature": [ - "({ fields, triggerId, metadata, dataGridRef, disabledActions, }: ", + "({ fields, triggerId, metadata, dataGridRef, disabledActionTypes, }: ", { "pluginId": "@kbn/cell-actions", "scope": "common", @@ -122,7 +585,7 @@ "id": "def-common.useDataGridColumnsCellActions.$1", "type": "Object", "tags": [], - "label": "{\n fields,\n triggerId,\n metadata,\n dataGridRef,\n disabledActions = [],\n}", + "label": "{\n fields,\n triggerId,\n metadata,\n dataGridRef,\n disabledActionTypes = [],\n}", "description": [], "signature": [ { @@ -191,7 +654,7 @@ "section": "def-common.CellActionCompatibilityContext", "text": "CellActionCompatibilityContext" }, - ") => Promise" + ") => Promise" ], "path": "packages/kbn-cell-actions/src/types.ts", "deprecated": false, @@ -212,7 +675,7 @@ "section": "def-common.CellActionCompatibilityContext", "text": "CellActionCompatibilityContext" }, - "" + "" ], "path": "packages/kbn-cell-actions/src/types.ts", "deprecated": false, @@ -231,7 +694,9 @@ "type": "Interface", "tags": [], "label": "CellActionCompatibilityContext", - "description": [], + "description": [ + "\nSubset of `CellActionExecutionContext` used only for the compatibility check in the `isCompatible` function.\nIt omits the references and the `field.value`." + ], "signature": [ { "pluginId": "@kbn/cell-actions", @@ -240,7 +705,7 @@ "section": "def-common.CellActionCompatibilityContext", "text": "CellActionCompatibilityContext" }, - " extends ", + " extends ", { "pluginId": "uiActions", "scope": "public", @@ -264,7 +729,7 @@ "\nThe object containing the field name and type, needed for the compatibility check" ], "signature": [ - "{ type: string; name: string; aggregatable?: boolean | undefined; }" + "{ [P in Exclude]: C[\"field\"][P]; }" ], "path": "packages/kbn-cell-actions/src/types.ts", "deprecated": false, @@ -280,7 +745,7 @@ "\nExtra configurations for actions." ], "signature": [ - "M | undefined" + "C[\"metadata\"] | undefined" ], "path": "packages/kbn-cell-actions/src/types.ts", "deprecated": false, @@ -358,7 +823,244 @@ "\nExtra configurations for actions." ], "signature": [ - "Record | undefined" + "Metadata | undefined" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionFactory", + "type": "Interface", + "tags": [], + "label": "CellActionFactory", + "description": [], + "signature": [ + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionFactory", + "text": "CellActionFactory" + }, + "" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionFactory.Unnamed", + "type": "Any", + "tags": [], + "label": "Unnamed", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionFactory.combine", + "type": "Function", + "tags": [], + "label": "combine", + "description": [], + "signature": [ + "
(partialActionTemplate: Partial<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionTemplate", + "text": "CellActionTemplate" + }, + ">) => ", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionFactory", + "text": "CellActionFactory" + }, + "" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionFactory.combine.$1", + "type": "Object", + "tags": [], + "label": "partialActionTemplate", + "description": [], + "signature": [ + "Partial<", + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionTemplate", + "text": "CellActionTemplate" + }, + ">" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps", + "type": "Interface", + "tags": [], + "label": "CellActionsProps", + "description": [], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.field", + "type": "Object", + "tags": [], + "label": "field", + "description": [ + "\nCommon set of properties used by most actions." + ], + "signature": [ + "CellActionField" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.triggerId", + "type": "string", + "tags": [], + "label": "triggerId", + "description": [ + "\nThe trigger in which the actions are registered." + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.mode", + "type": "Enum", + "tags": [], + "label": "mode", + "description": [ + "\nUI configuration. Possible options are `HOVER` and `INLINE`.\n\n`HOVER` shows the actions when the children component is hovered.\n\n`INLINE` always shows the actions." + ], + "signature": [ + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionsMode", + "text": "CellActionsMode" + } + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.showActionTooltips", + "type": "CompoundType", + "tags": [], + "label": "showActionTooltips", + "description": [ + "\nIt displays a tooltip for every action button when `true`." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.visibleCellActions", + "type": "number", + "tags": [], + "label": "visibleCellActions", + "description": [ + "\nIt shows 'more actions' button when the number of actions is bigger than this parameter." + ], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.disabledActionTypes", + "type": "Array", + "tags": [], + "label": "disabledActionTypes", + "description": [ + "\nList of Actions ids that shouldn't be displayed inside cell actions." + ], + "signature": [ + "string[] | undefined" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.metadata", + "type": "Object", + "tags": [], + "label": "metadata", + "description": [ + "\nCustom set of properties used by some actions.\nAn action might require a specific set of metadata properties to render.\nThis data is sent directly to actions." + ], + "signature": [ + "Metadata | undefined" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionsProps.className", + "type": "string", + "tags": [], + "label": "className", + "description": [], + "signature": [ + "string | undefined" ], "path": "packages/kbn-cell-actions/src/types.ts", "deprecated": false, @@ -383,8 +1085,14 @@ "text": "UseDataGridColumnsCellActionsProps" }, " extends Pick<", - "CellActionsProps", - ", \"metadata\" | \"disabledActions\" | \"triggerId\">" + { + "pluginId": "@kbn/cell-actions", + "scope": "common", + "docId": "kibKbnCellActionsPluginApi", + "section": "def-common.CellActionsProps", + "text": "CellActionsProps" + }, + ", \"metadata\" | \"triggerId\" | \"disabledActionTypes\">" ], "path": "packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx", "deprecated": false, @@ -432,13 +1140,112 @@ "tags": [], "label": "CellActionsMode", "description": [], + "path": "packages/kbn-cell-actions/src/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionExtend", + "type": "Type", + "tags": [], + "label": "CellActionExtend", + "description": [ + "\nAction factory extend parameter type," + ], + "signature": [ + "Partial & { id: string; }" + ], + "path": "packages/kbn-cell-actions/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.CellActionTemplate", + "type": "Type", + "tags": [], + "label": "CellActionTemplate", + "description": [ + "\nCell action factory template with optional `id`.\nThe id override is required when using the action factory so it\ncan be omitted in the original action creator" + ], + "signature": [ + "{ [P in Exclude]: C[P]; }" + ], "path": "packages/kbn-cell-actions/src/types.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.COPY_CELL_ACTION_TYPE", + "type": "string", + "tags": [], + "label": "COPY_CELL_ACTION_TYPE", + "description": [], + "signature": [ + "\"cellAction-copy\"" + ], + "path": "packages/kbn-cell-actions/src/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.FILTER_CELL_ACTION_TYPE", + "type": "string", + "tags": [], + "label": "FILTER_CELL_ACTION_TYPE", + "description": [], + "signature": [ + "\"cellAction-filter\"" + ], + "path": "packages/kbn-cell-actions/src/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.UseDataGridColumnsCellActions", + "type": "Type", + "tags": [], + "label": "UseDataGridColumnsCellActions", + "description": [], + "signature": [ + "(props: P) => ", + "EuiDataGridColumnCellAction", + "[][]" + ], + "path": "packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/cell-actions", + "id": "def-common.UseDataGridColumnsCellActions.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ], - "misc": [], "objects": [] } } \ No newline at end of file diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index e5cfc270cef23..da4ff62497a80 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-threat-hunting-explore](https://github.com/orgs/elast | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 20 | 0 | 15 | 3 | +| 57 | 1 | 42 | 2 | ## Common @@ -34,3 +34,6 @@ Contact [@elastic/security-threat-hunting-explore](https://github.com/orgs/elast ### Enums +### Consts, variables and types + + diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index 15dd0fdbe3332..17064fabba6b5 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 2e4c2c4f26f45..a60e53f32a4cd 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index a2aecf0935506..68de0b62c5a18 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index c003bd0eb8880..0e23290944f23 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index efd90cdb8c7d3..7eaab16696ac0 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 3b180645a0e46..e813b270b05ad 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 773245cbab5e6..8335aa8cff0db 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index aacc45201f2fc..a76d50d334c99 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index dccf85580b0c5..9cab1e3bdd9e1 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index fc9fedc6f8065..5075ef437b7f4 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 34516a90a66e9..07d4a5ad4cb32 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 7a65d1d6ad1af..831b6c4fcdb46 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 7c42d390232ce..e3861fec754c9 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 23554474e8b7f..affaac3e161a1 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 296d6229650af..a09d2e8bea242 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index e1a1ad549969d..f5219f7fc0df8 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index a38ddde4e6af9..12aadc3f5deed 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 289de330ab691..444b341061a62 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index 1787d4e5ed4af..7a4e26b857c35 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 1031230ab6715..c4cecd592eacc 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 528b7b8acc977..f05396da060f6 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index cc52ac11e028c..660446ef7de3a 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index f0b1476308013..58c0e76feab1c 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index b66748ea6e4c9..8bd8e48fd4d40 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index af89d9f3679de..8f4ab9c447eb1 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 08ff5bc332a2f..9bbe05de78d9c 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 6d8c0fd7aa201..05d50e1f0ffc2 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index b1ce3bf43e0c7..1a8ca86255726 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 24133f929d7c5..8e40919cc1bf5 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index a8c9d85345368..1693575cbe2c5 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 6f4b48e0b8c53..f702115a5ac99 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 7c763c61c916a..3f06a33b7dcf7 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index e38960da7106a..58fd806777089 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index e1531edb69605..5f93533d4d475 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 34bc7108d59db..79e1e625d5b0b 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 4fea5f739e393..ea47ce0af3677 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; -Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) for questions regarding this plugin. +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 701072c9741a9..af1a5a0dafe59 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,14 +8,14 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; -Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) for questions regarding this plugin. +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. **Code health stats** diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 3dc95aea36589..6362f2d68afa5 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 65bcdd7d4ccf1..751b8ceb6997e 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 81b8f50698dbd..0970bfed9dfd1 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 410082c60c589..242cc78e194e4 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 7bd422b186eeb..cb104de0f3a91 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 154344d2b1bf3..4548203f35438 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 46dc145ac5277..34f15270bcc59 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index ecd9f7905b8ef..4f135027be15b 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 0002f9f8b14b4..5e435db4ed9f2 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 349aa4d53ba7c..67dd2b40c8849 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index ff47688415cad..6d42570d313d9 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 1d0e7e2430a41..a36203ad8d7e8 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index ab7743fff6190..e04f2c8cbf322 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index 5d62a10f68667..439a58d3a11c0 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index 980a1e6c91d31..6a563922f9daa 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 972413d73acb6..75c5f7bb938b7 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index fa94ec1869312..44840c821f5e5 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 71d57a4aa7bf3..64969c735503d 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 4236cbd64bddb..2dd8383a610d5 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index dd83440f39366..38966007acf5b 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 36b28c52a0fe8..3bd1df9846b8b 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index 9985de2ee5ae6..631f9c532d630 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index b7af656985320..b818d2619b912 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 24efc2b93dd65..5123ca8672c48 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 442daa4975a2c..d4e4417a37bf9 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 7a02289568214..cf67ec9c49b6e 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index bb015ef77b66d..6fa0f62e7c061 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 3c5ec2362b54c..2c0c51bbb9fa9 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index a1df8a7f491f1..72467bb10b468 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index afcc716e20bdd..a10a0976d2a25 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index a5a6985e33c26..634c603bb3736 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 1b8f1096b7bab..37f5bbcfce110 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index f380b1cfb0425..99060386eabee 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 8ce93ce406b39..d8d41b071e5cb 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index c8ba1e9d8a9b1..6ca44e245f46e 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 28e062f4fe0eb..d87730c83727f 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 9f83827ac8e68..705c70bb5e5ab 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 75c865aea8339..f9133dc61749e 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 0c3614a9b509d..c7259953ec24d 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index ec9d76416b325..78f90f7282dd9 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index d7ebd416b1324..27dbea1868281 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 482f705fe1bb9..9911307138221 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index fe023434d8a5c..78bdee40138a6 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 5ba62f38ad996..eb27ccf7cbc01 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index ccfb48ebcb122..173c69eddff16 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 9d64aa7f23222..fc9226ec23be3 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index ceb5a7c0be421..7ce0427e580c1 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index d646c4c118f4c..59a8a8a986d60 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 7f583df8df720..9a9f3a39d847f 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index ebaef136c3064..56435821ea369 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index b95d169bcd598..0e1d73497c91e 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index c348a9498db1b..4283549d44b6a 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 95b234ae04d7f..dbae1eb5468c4 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index d19061a224a7d..1fead6619e9bf 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 87cc07251f7cb..1b23e10c1806e 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 3b4ed968ba0fd..1070e020a552b 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index 92c01c71ec7c6..d5d61b95e8a64 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index d07a7bb53d7dc..ec5a983e343d2 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index 82e76937344ec..b462c1a7b8784 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 777e6b75d2e9a..de0633c626b8c 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index 3336cae16e4ff..c1bc57c3c55ef 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 20237828ca57b..2a2be06bffdf3 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 7eb21bbec97ee..694bff205ef67 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 2ab58adf94550..fa6b6deac04e7 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index c9dcf00485903..28a3dfe8e7b6a 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index ea886fc2c5575..914ad4cbd9779 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 21c311d7a15fd..7ad9c4c857823 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index ef2f8309fbe7f..916241b138610 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 3dbd6a125765b..b397d7e6d42fc 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 067012b10fa9b..7559367cad6e6 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 87dc5a1b40d0e..c3b34f91967fa 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index af0f4a1526128..8059612a8bd69 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index e60468d5138a1..4530ad8ae700e 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 95eedd2607f5b..c0c54e87c07f5 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 8c330bc925504..70edb49e35926 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 6a71ac221bcbd..bb1d3ec670f6b 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 31fe77a8b7c92..c759d1dbe2306 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 23d41aaf84d72..21f5e0487657f 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index fc42707827a80..49cdf84b8a81d 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 085041fc20a2c..d2f6135b86b98 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 975186d955c74..3662d97f6916f 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 7f9792e55849a..735deb2923ddc 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index fa0ce935e4335..da0ef8f5cbf4d 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index dcafa57c953b4..7f05061343161 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 9659d217ffacf..1bb5d6f6bda0e 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 0e096508cc902..978edbf1e331e 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index f72d184c96920..0d641275140a5 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 92077b691f772..238337e875534 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 7a9fc848f01a4..207c50bbbb8c7 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index 650613ae2aa74..9ed3166096ace 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 74483bc5daa19..6aabab831ddcb 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 168cad329b504..ecbeabe4e3065 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.devdocs.json b/api_docs/kbn_core_saved_objects_api_server.devdocs.json index eba841b82db65..3017f0b867d78 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -2071,6 +2071,601 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject", + "type": "Interface", + "tags": [], + "label": "SavedObject", + "description": [ + "\nDefinition of the Saved Object interface\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObject", + "text": "SavedObject" + }, + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The ID of this Saved Object, guaranteed to be unique for all objects of the same `type`" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.type", + "type": "string", + "tags": [], + "label": "type", + "description": [ + " The type of Saved Object. Each plugin can define it's own custom Saved Object types." + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.version", + "type": "string", + "tags": [], + "label": "version", + "description": [ + "An opaque version number which changes on each successful write operation. Can be used for implementing optimistic concurrency control." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.created_at", + "type": "string", + "tags": [], + "label": "created_at", + "description": [ + "Timestamp of the time this document had been created." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.updated_at", + "type": "string", + "tags": [], + "label": "updated_at", + "description": [ + "Timestamp of the last time this document had been updated." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.error", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "Error associated with this object, populated if an operation failed for this object." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectError", + "text": "SavedObjectError" + }, + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.attributes", + "type": "Uncategorized", + "tags": [], + "label": "attributes", + "description": [ + "The data for a Saved Object is stored as an object in the `attributes` property." + ], + "signature": [ + "T" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.references", + "type": "Array", + "tags": [], + "label": "references", + "description": [ + "{@inheritdoc SavedObjectReference}" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectReference", + "text": "SavedObjectReference" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.migrationVersion", + "type": "Object", + "tags": [], + "label": "migrationVersion", + "description": [ + "{@inheritdoc SavedObjectsMigrationVersion}" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectsMigrationVersion", + "text": "SavedObjectsMigrationVersion" + }, + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.coreMigrationVersion", + "type": "string", + "tags": [], + "label": "coreMigrationVersion", + "description": [ + "A semver value that is used when upgrading objects between Kibana versions." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.namespaces", + "type": "Array", + "tags": [], + "label": "namespaces", + "description": [ + "\nSpace(s) that this saved object exists in. This attribute is not used for \"global\" saved object types which are registered with\n`namespaceType: 'agnostic'`." + ], + "signature": [ + "string[] | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObject.originId", + "type": "string", + "tags": [], + "label": "originId", + "description": [ + "\nThe ID of the saved object this originated from. This is set if this object's `id` was regenerated; that can happen during migration\nfrom a legacy single-namespace type, or during import. It is only set during migration or create operations. This is used during import\nto ensure that ID regeneration is deterministic, so saved objects will be overwritten if they are imported multiple times into a given\nspace." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectAttributes", + "type": "Interface", + "tags": [ + "deprecated" + ], + "label": "SavedObjectAttributes", + "description": [ + "\nThe data for a Saved Object is stored as an object in the `attributes`\nproperty.\n" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": true, + "trackAdoption": false, + "references": [ + { + "plugin": "@kbn/core-saved-objects-common", + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts" + }, + { + "plugin": "@kbn/core-saved-objects-common", + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts" + }, + { + "plugin": "@kbn/core-saved-objects-common", + "path": "packages/core/saved-objects/core-saved-objects-common/src/saved_objects.ts" + }, + { + "plugin": "@kbn/core-saved-objects-server", + "path": "packages/core/saved-objects/core-saved-objects-server/index.ts" + }, + { + "plugin": "@kbn/core", + "path": "src/core/server/index.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/actions_client.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/actions_client.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/actions_client.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/actions_client.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/actions_client.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/actions_client.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/actions_client.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/types.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/types.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/types.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/types.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/types.ts" + }, + { + "plugin": "actions", + "path": "x-pack/plugins/actions/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/common/rule.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/rules_client/common/inject_references.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/rules_client/common/inject_references.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/types.ts" + }, + { + "plugin": "canvas", + "path": "x-pack/plugins/canvas/server/routes/custom_elements/find.ts" + }, + { + "plugin": "canvas", + "path": "x-pack/plugins/canvas/server/routes/custom_elements/find.ts" + }, + { + "plugin": "canvas", + "path": "x-pack/plugins/canvas/server/routes/workpad/find.ts" + }, + { + "plugin": "canvas", + "path": "x-pack/plugins/canvas/server/routes/workpad/find.ts" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_types.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/rule_actions/legacy_migrations.ts" + }, + { + "plugin": "taskManager", + "path": "x-pack/plugins/task_manager/server/task_store.test.ts" + }, + { + "plugin": "taskManager", + "path": "x-pack/plugins/task_manager/server/task_store.test.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/dashboard_saved_object/migrations/migrate_extract_panel_references.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/dashboard_telemetry_collection_task.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/dashboard_telemetry.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/dashboard_telemetry.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts" + }, + { + "plugin": "dashboard", + "path": "src/plugins/dashboard/server/usage/find_by_value_embeddables.ts" + }, + { + "plugin": "savedSearch", + "path": "src/plugins/saved_search/server/saved_objects/search_migrations.ts" + }, + { + "plugin": "savedSearch", + "path": "src/plugins/saved_search/server/saved_objects/search_migrations.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/saved_objects/migrations/7.11/index.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/saved_objects/migrations/7.11/index.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/saved_objects/migrations/7.15/index.ts" + }, + { + "plugin": "alerting", + "path": "x-pack/plugins/alerting/server/saved_objects/migrations/7.15/index.ts" + }, + { + "plugin": "@kbn/core-saved-objects-server-internal", + "path": "packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts" + }, + { + "plugin": "@kbn/core-saved-objects-server-internal", + "path": "packages/core/saved-objects/core-saved-objects-server-internal/src/routes/legacy_import_export/lib/collect_references_deep.test.ts" + } + ], + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectAttributes.Unnamed", + "type": "IndexSignature", + "tags": [], + "label": "[key: string]: SavedObjectAttribute", + "description": [], + "signature": [ + "[key: string]: ", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectAttribute", + "text": "SavedObjectAttribute" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectReference", + "type": "Interface", + "tags": [], + "label": "SavedObjectReference", + "description": [ + "\nA reference to another saved object.\n" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectReference.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectReference.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectReference.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-api-server", "id": "def-common.SavedObjectReferenceWithContext", @@ -6947,6 +7542,63 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectAttribute", + "type": "Type", + "tags": [], + "label": "SavedObjectAttribute", + "description": [ + "\nType definition for a Saved Object attribute value\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectAttributeSingle", + "text": "SavedObjectAttributeSingle" + }, + " | ", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectAttributeSingle", + "text": "SavedObjectAttributeSingle" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-api-server", + "id": "def-common.SavedObjectAttributeSingle", + "type": "Type", + "tags": [], + "label": "SavedObjectAttributeSingle", + "description": [ + "\nDon't use this type, it's simply a helper type for {@link SavedObjectAttribute}\n" + ], + "signature": [ + "string | number | boolean | ", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectAttributes", + "text": "SavedObjectAttributes" + }, + " | null | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/server_types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-api-server", "id": "def-common.SavedObjectsClosePointInTimeOptions", diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 617fca8de7507..dbc1314565879 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 313 | 1 | 0 | 1 | +| 334 | 1 | 4 | 1 | ## Common diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index a68c642df780c..8772394c5e46f 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index b5acd722110cc..9c4fa5819cce4 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json index 81e9492f9c625..8fce5f63df9dd 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json @@ -141,6 +141,41 @@ } ], "functions": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.assertValidModelVersion", + "type": "Function", + "tags": [], + "label": "assertValidModelVersion", + "description": [ + "\nAsserts the provided number or string is a valid model version, and returns it.\n\nA valid model version is a positive integer.\n" + ], + "signature": [ + "(modelVersion: string | number) => number" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.assertValidModelVersion.$1", + "type": "CompoundType", + "tags": [], + "label": "modelVersion", + "description": [], + "signature": [ + "string | number" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.decodeRequestVersion", @@ -551,11 +586,133 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.isVirtualModelVersion", + "type": "Function", + "tags": [], + "label": "isVirtualModelVersion", + "description": [ + "\nReturn true if the given semver version is a virtual model version.\nVirtual model versions are version which major is the {@link modelVersionVirtualMajor}\n" + ], + "signature": [ + "(version: string) => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.isVirtualModelVersion.$1", + "type": "string", + "tags": [], + "label": "version", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.modelVersionToVirtualVersion", + "type": "Function", + "tags": [], + "label": "modelVersionToVirtualVersion", + "description": [ + "\nReturns the virtual version associated with the given model version\n" + ], + "signature": [ + "(modelVersion: string | number) => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.modelVersionToVirtualVersion.$1", + "type": "CompoundType", + "tags": [], + "label": "modelVersion", + "description": [], + "signature": [ + "string | number" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.virtualVersionToModelVersion", + "type": "Function", + "tags": [], + "label": "virtualVersionToModelVersion", + "description": [ + "\nConverts a virtual model version to its model version.\n" + ], + "signature": [ + "(virtualVersion: string) => number" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.virtualVersionToModelVersion.$1", + "type": "string", + "tags": [], + "label": "virtualVersion", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.modelVersionVirtualMajor", + "type": "number", + "tags": [], + "label": "modelVersionVirtualMajor", + "description": [ + "\nThe major version that is used to represent model versions." + ], + "signature": [ + "10" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.SavedObjectsConfigType", diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 2b0eaa47d18b5..23c6c35c2039c 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 37 | 0 | 31 | 6 | +| 46 | 0 | 35 | 6 | ## Common diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index b5725d6b908f6..651e7e4097aa4 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index cde4e4e9f3815..c154f02694307 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 6c8aab5b173a6..5ae5a82ee4237 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 336bf48fdf9a6..fdcf9166372ff 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.devdocs.json b/api_docs/kbn_core_saved_objects_common.devdocs.json index ff0bd2270c69f..200162830bb97 100644 --- a/api_docs/kbn_core_saved_objects_common.devdocs.json +++ b/api_docs/kbn_core_saved_objects_common.devdocs.json @@ -20,6 +20,61 @@ "classes": [], "functions": [], "interfaces": [ + { + "parentPluginId": "@kbn/core-saved-objects-common", + "id": "def-common.LegacyUrlAliasTarget", + "type": "Interface", + "tags": [], + "label": "LegacyUrlAliasTarget", + "description": [ + "\nClient interface for interacting with legacy URL aliases." + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-common", + "id": "def-common.LegacyUrlAliasTarget.targetSpace", + "type": "string", + "tags": [], + "label": "targetSpace", + "description": [ + "\nThe namespace that the object existed in when it was converted." + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-common", + "id": "def-common.LegacyUrlAliasTarget.targetType", + "type": "string", + "tags": [], + "label": "targetType", + "description": [ + "\nThe type of the object when it was converted." + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-common", + "id": "def-common.LegacyUrlAliasTarget.sourceId", + "type": "string", + "tags": [], + "label": "sourceId", + "description": [ + "\nThe original ID of the object, before it was converted." + ], + "path": "packages/core/saved-objects/core-saved-objects-common/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-common", "id": "def-common.SavedObjectError", @@ -1004,38 +1059,6 @@ "plugin": "@kbn/core-saved-objects-api-browser", "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/simple_saved_object.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/base.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/resolve.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/find.ts" - }, { "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" @@ -1052,18 +1075,6 @@ "plugin": "@kbn/core-saved-objects-api-server", "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_repository.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/saved_objects_client.ts" - }, { "plugin": "@kbn/core-saved-objects-browser-internal", "path": "packages/core/saved-objects/core-saved-objects-browser-internal/src/simple_saved_object.ts" @@ -1444,18 +1455,6 @@ "plugin": "@kbn/core-saved-objects-browser-mocks", "path": "packages/core/saved-objects/core-saved-objects-browser-mocks/src/simple_saved_object.mock.ts" }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, - { - "plugin": "@kbn/core-saved-objects-server", - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts" - }, { "plugin": "@kbn/core-saved-objects-import-export-server-internal", "path": "packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/errors.ts" @@ -2353,34 +2352,6 @@ "plugin": "@kbn/core-saved-objects-api-browser", "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/apis/create.ts" }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/bulk_create.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/update.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts" - }, - { - "plugin": "@kbn/core-saved-objects-api-server", - "path": "packages/core/saved-objects/core-saved-objects-api-server/src/apis/create.ts" - }, { "plugin": "@kbn/core-saved-objects-api-browser", "path": "packages/core/saved-objects/core-saved-objects-api-browser/src/apis/bulk_update.ts" diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index e67c8a9b0590c..b7b195fba3528 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 67 | 0 | 39 | 0 | +| 71 | 0 | 39 | 0 | ## Common diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 0c9167b4a73a2..a38e20bace754 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 46279a0edf84c..549bf381786d7 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json index e9d8d1afb191e..68bab1e33dbd5 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json @@ -69,7 +69,7 @@ "id": "def-common.DocumentMigrator.Unnamed.$1", "type": "Object", "tags": [], - "label": "{ typeRegistry, kibanaVersion, convertVersion, log }", + "label": "documentMigratorOptions", "description": [], "signature": [ "DocumentMigratorOptions" diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index f14e4ece55ede..8c20317335a2c 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 32b2940ccb0e9..6a8c24a0190d1 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.devdocs.json b/api_docs/kbn_core_saved_objects_server.devdocs.json index 1f8c44729ed82..b90aa577dd1ab 100644 --- a/api_docs/kbn_core_saved_objects_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_server.devdocs.json @@ -17,283 +17,3412 @@ "objects": [] }, "common": { - "classes": [], - "functions": [], - "interfaces": [ + "classes": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AddAuditEventParams", - "type": "Interface", + "id": "def-common.SavedObjectsErrorHelpers", + "type": "Class", "tags": [], - "label": "AddAuditEventParams", + "label": "SavedObjectsErrorHelpers", "description": [ - "\nThe AddAuditEventParams interface contains settings for adding\naudit events via the ISavedObjectsSecurityExtension." + "\nThe SavedObjectsErrorHelpers class is a simple class for creating, decorating, and\nqualifying saved object errors." ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AddAuditEventParams.action", - "type": "Enum", + "id": "def-common.SavedObjectsErrorHelpers.isSavedObjectsClientError", + "type": "Function", "tags": [], - "label": "action", + "label": "isSavedObjectsClientError", "description": [ - "\nThe relevant action" + "\nDetermines if an error is a saved objects client error" ], "signature": [ + "(error: any) => error is ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.AuditAction", - "text": "AuditAction" + "section": "def-common.DecoratedError", + "text": "DecoratedError" } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isSavedObjectsClientError.$1", + "type": "Any", + "tags": [], + "label": "error", + "description": [ + "the error to check" + ], + "signature": [ + "any" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a saved objects client error" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AddAuditEventParams.outcome", - "type": "string", + "id": "def-common.SavedObjectsErrorHelpers.decorateBadRequestError", + "type": "Function", "tags": [], - "label": "outcome", + "label": "decorateBadRequestError", "description": [ - "\nThe outcome of the operation\n'failure' | 'success' | 'unknown'" + "\nDecorates a bad request error (400) by adding a reason" ], "signature": [ - "string | undefined" + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateBadRequestError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateBadRequestError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the bad request (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AddAuditEventParams.savedObject", - "type": "Object", + "id": "def-common.SavedObjectsErrorHelpers.createBadRequestError", + "type": "Function", "tags": [], - "label": "savedObject", + "label": "createBadRequestError", "description": [ - "\nrelevant saved object information\nobject containing type & id strings" + "\nCreates a decorated bad request error (400). Bad requests come in a few flavors:\nunsupported type, invalid version, elastic search cannot execute script, or plain\nvanilla bad request." ], "signature": [ - "{ type: string; id: string; } | undefined" + "(reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createBadRequestError.$1", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the bad request (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AddAuditEventParams.addToSpaces", - "type": "Object", + "id": "def-common.SavedObjectsErrorHelpers.createUnsupportedTypeError", + "type": "Function", "tags": [], - "label": "addToSpaces", + "label": "createUnsupportedTypeError", "description": [ - "\nArray of spaces being added. For\nUPDATE_OBJECTS_SPACES action only" + "\nCreates a decorated unsupported type error (flavor of bad request 400)" ], "signature": [ - "readonly string[] | undefined" + "(type: string) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createUnsupportedTypeError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "the unsupported saved object type" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "the decorated error" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AddAuditEventParams.deleteFromSpaces", - "type": "Object", + "id": "def-common.SavedObjectsErrorHelpers.isBadRequestError", + "type": "Function", "tags": [], - "label": "deleteFromSpaces", + "label": "isBadRequestError", "description": [ - "\nArray of spaces being removed. For\nUPDATE_OBJECTS_SPACES action only" + "\nDetermines if an error is a bad request error (400)" ], "signature": [ - "readonly string[] | undefined" + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isBadRequestError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a bad request error" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AddAuditEventParams.error", - "type": "Object", + "id": "def-common.SavedObjectsErrorHelpers.createInvalidVersionError", + "type": "Function", "tags": [], - "label": "error", + "label": "createInvalidVersionError", "description": [ - "\nrelevant error information to add to\nthe audit event" + "\nCreates a decorated invalid version error (flavor of bad request 400)" ], "signature": [ - "Error | undefined" + "(versionInput?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AuthorizationTypeEntry", - "type": "Interface", - "tags": [], - "label": "AuthorizationTypeEntry", - "description": [ - "\nThe AuthorizationTypeEntry interface contains space-related details\nfor CheckAuthorizationResults." - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createInvalidVersionError.$1", + "type": "string", + "tags": [], + "label": "versionInput", + "description": [ + "the version string (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AuthorizationTypeEntry.authorizedSpaces", - "type": "Array", + "id": "def-common.SavedObjectsErrorHelpers.isInvalidVersionError", + "type": "Function", "tags": [], - "label": "authorizedSpaces", + "label": "isInvalidVersionError", "description": [ - "\nAn array of authorized spaces for the associated type/action\nin the associated record/map." + "\nDetermines if an error is an invalid version error (flavor of bad request 400)" ], "signature": [ - "string[]" + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isInvalidVersionError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is an invalid version error" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AuthorizationTypeEntry.isGloballyAuthorized", - "type": "CompoundType", + "id": "def-common.SavedObjectsErrorHelpers.decorateNotAuthorizedError", + "type": "Function", "tags": [], - "label": "isGloballyAuthorized", + "label": "decorateNotAuthorizedError", "description": [ - "\nIs the associated type/action globally authorized?" + "\nDecorates an error as an not authorized error (401)" ], "signature": [ - "boolean | undefined" + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationParams", + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the not authorized error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isNotAuthorizedError", + "type": "Function", + "tags": [], + "label": "isNotAuthorizedError", + "description": [ + "\nDetermines if an error is a not authorized error (401)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isNotAuthorizedError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a not authorized error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateForbiddenError", + "type": "Function", + "tags": [], + "label": "decorateForbiddenError", + "description": [ + "\nDecorates an error as a forbidden error (403)" + ], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateForbiddenError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateForbiddenError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the forbidden error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isForbiddenError", + "type": "Function", + "tags": [], + "label": "isForbiddenError", + "description": [ + "\nDetermines if an error is a forbidden error (403)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isForbiddenError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a forbidden error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError", + "type": "Function", + "tags": [], + "label": "decorateRequestEntityTooLargeError", + "description": [ + "\nDecorates a request entity too large error (413)" + ], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the request entity too large error" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isRequestEntityTooLargeError", + "type": "Function", + "tags": [], + "label": "isRequestEntityTooLargeError", + "description": [ + "\nDetermines if an error is a request entity too large error(413)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isRequestEntityTooLargeError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a request entity too large error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundError", + "type": "Function", + "tags": [], + "label": "createGenericNotFoundError", + "description": [ + "\nCreates a generic not found error (404)" + ], + "signature": [ + "(type?: string | null, id?: string | null) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundError.$1", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [ + "the saved object type or null (default is null)" + ], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundError.$2", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [ + "the saved object id or null (default is null)" + ], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createIndexAliasNotFoundError", + "type": "Function", + "tags": [], + "label": "createIndexAliasNotFoundError", + "description": [ + "\nCreates an alias not found error (flavor of general error 500)" + ], + "signature": [ + "(alias: string) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createIndexAliasNotFoundError.$1", + "type": "string", + "tags": [], + "label": "alias", + "description": [ + "the unfound saved object alias" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError", + "type": "Function", + "tags": [], + "label": "decorateIndexAliasNotFoundError", + "description": [ + "\nDecorates an index alias not found error (flavor of general error 500)" + ], + "signature": [ + "(error: Error, alias: string) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$2", + "type": "string", + "tags": [], + "label": "alias", + "description": [ + "the unfound index alias" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isNotFoundError", + "type": "Function", + "tags": [], + "label": "isNotFoundError", + "description": [ + "\nDetermines if an error is a not found error (404)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isNotFoundError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a not found error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateConflictError", + "type": "Function", + "tags": [], + "label": "decorateConflictError", + "description": [ + "\nDecorates a conflict error (409)" + ], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateConflictError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateConflictError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the conflict error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createConflictError", + "type": "Function", + "tags": [], + "label": "createConflictError", + "description": [ + "\nCreates a conflict error (409)" + ], + "signature": [ + "(type: string, id: string, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createConflictError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "the saved object type" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createConflictError.$2", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "the saved object id" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createConflictError.$3", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the conflict error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isConflictError", + "type": "Function", + "tags": [], + "label": "isConflictError", + "description": [ + "\nDetermines if an error is a conflict error (409)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isConflictError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a conflict error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "decorateTooManyRequestsError", + "description": [ + "\nDecorates a too many requests error (429)" + ], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the too many requests error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "createTooManyRequestsError", + "description": [ + "\nCreates a too many requests error (429)" + ], + "signature": [ + "(type: string, id: string) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createTooManyRequestsError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "the saved object type" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createTooManyRequestsError.$2", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "the saved object id" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "isTooManyRequestsError", + "description": [ + "\nDetermines if an error is a too many requests error (429)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isTooManyRequestsError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a too many requests error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError", + "type": "Function", + "tags": [], + "label": "decorateEsCannotExecuteScriptError", + "description": [ + "\nDecorates an elastic search cannot execute script error (flavor of 400)" + ], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the cannot execute error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError", + "type": "Function", + "tags": [], + "label": "isEsCannotExecuteScriptError", + "description": [ + "\nDetermines if an error is an elastic search cannot execute script error (flavor of 400)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a cannot execute error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateEsUnavailableError", + "type": "Function", + "tags": [], + "label": "decorateEsUnavailableError", + "description": [ + "\nDecorates an elastic search unavailable error (503)" + ], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateEsUnavailableError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateEsUnavailableError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the elastic search unavailable error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isEsUnavailableError", + "type": "Function", + "tags": [], + "label": "isEsUnavailableError", + "description": [ + "\nDetermines if an error is an elastic search unavailable error (flavor of 400)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isEsUnavailableError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is an elastic search unavailable error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateGeneralError", + "type": "Function", + "tags": [], + "label": "decorateGeneralError", + "description": [ + "\nDecorates a general error (500)" + ], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateGeneralError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "the error to decorate" + ], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.decorateGeneralError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [ + "the reason for the error (optional)" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isGeneralError", + "type": "Function", + "tags": [], + "label": "isGeneralError", + "description": [ + "\nDetermines if an error is a general error (500)" + ], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.isGeneralError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "the error or decorated error" + ], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean - true if error is a general error" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError", + "type": "Function", + "tags": [], + "label": "createGenericNotFoundEsUnavailableError", + "description": [ + "\nCreates a generic elastic search not present error" + ], + "signature": [ + "(type?: string | null, id?: string | null) => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$1", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [ + "the saved object type or null, default null" + ], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$2", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [ + "the saved object id or null, default null" + ], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "the decorated error" + ] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizationTypeEntry", + "type": "Interface", + "tags": [], + "label": "AuthorizationTypeEntry", + "description": [ + "\nThe AuthorizationTypeEntry interface contains space-related details\nfor CheckAuthorizationResults." + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizationTypeEntry.authorizedSpaces", + "type": "Array", + "tags": [], + "label": "authorizedSpaces", + "description": [ + "\nAn array of authorized spaces for the associated type/action\nin the associated record/map." + ], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizationTypeEntry.isGloballyAuthorized", + "type": "CompoundType", + "tags": [], + "label": "isGloballyAuthorized", + "description": [ + "\nIs the associated type/action globally authorized?" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeAndRedactInternalBulkResolveParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeAndRedactInternalBulkResolveParams", + "description": [ + "\nThe AuthorizeAndRedactInternalBulkResolveParams interface extends\nAuthorizeParams and is used for the AuthorizeAndRedactInternalBulkResolve\nmethod of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeAndRedactInternalBulkResolveParams", + "text": "AuthorizeAndRedactInternalBulkResolveParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeAndRedactInternalBulkResolveParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "\nThe objects to authorize" + ], + "signature": [ + "(", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.BulkResolveError", + "text": "BulkResolveError" + }, + " | ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsResolveResponse", + "text": "SavedObjectsResolveResponse" + }, + ")[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeAndRedactMultiNamespaceReferencesParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeAndRedactMultiNamespaceReferencesParams", + "description": [ + "\nThe AuthorizeAndRedactMultiNamespaceReferencesParams interface extends\nAuthorizeParams and is used for the AuthorizeAndRedactMultiNamespaceReferences\nmethod of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeAndRedactMultiNamespaceReferencesParams", + "text": "AuthorizeAndRedactMultiNamespaceReferencesParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeAndRedactMultiNamespaceReferencesParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "The objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectReferenceWithContext", + "text": "SavedObjectReferenceWithContext" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeAndRedactMultiNamespaceReferencesParams.options", + "type": "Object", + "tags": [], + "label": "options", + "description": [ + "\noptions for the operation\n- purpose: 'collectMultiNamespaceReferences' or 'updateObjectsSpaces'\ndefault purpose is 'collectMultiNamespaceReferences'." + ], + "signature": [ + "MultiNamespaceReferencesOptions", + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkCreateParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeBulkCreateParams", + "description": [ + "\nThe AuthorizeBulkCreateParams interface extends AuthorizeParams and is\nused for the AuthorizeBulkCreate method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkCreateParams", + "text": "AuthorizeBulkCreateParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkCreateParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "The objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCreateObject", + "text": "AuthorizeCreateObject" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkDeleteParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeBulkDeleteParams", + "description": [ + "\nThe AuthorizeBulkDeleteParams interface extends AuthorizeParams and is\nused for the AuthorizeBulkDelete method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkDeleteParams", + "text": "AuthorizeBulkDeleteParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkDeleteParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "The objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkGetObject", + "type": "Interface", + "tags": [], + "label": "AuthorizeBulkGetObject", + "description": [ + "\nThe AuthorizeBulkGetObject interface extends AuthorizeObjectWithExistingSpaces\nand contains a object namespaces override. Used by the\nauthorizeBulkGet method." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkGetObject", + "text": "AuthorizeBulkGetObject" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkGetObject.objectNamespaces", + "type": "Array", + "tags": [], + "label": "objectNamespaces", + "description": [ + "\nThe namespaces to include when retrieving this object. Populated by options\npassed to the repository's update or bulkUpdate method." + ], + "signature": [ + "string[] | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkGetObject.error", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [ + "\nWhether or not an error occurred when getting this object. Populated by\nthe result of a query. Default is false." + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkGetParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeBulkGetParams", + "description": [ + "\nThe AuthorizeBulkGetParams interface extends AuthorizeParams and is\nused for the AuthorizeBulkGet method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkGetParams", + "text": "AuthorizeBulkGetParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkGetParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "The objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkGetObject", + "text": "AuthorizeBulkGetObject" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkUpdateParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeBulkUpdateParams", + "description": [ + "\nThe AuthorizeBulkUpdateParams interface extends AuthorizeParams and is\nused for the AuthorizeBulkUpdate method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkUpdateParams", + "text": "AuthorizeBulkUpdateParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeBulkUpdateParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "The objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeUpdateObject", + "text": "AuthorizeUpdateObject" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeCheckConflictsParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeCheckConflictsParams", + "description": [ + "\nThe AuthorizeCheckConflictsParams interface extends AuthorizeParams and is\nused for the AuthorizeCheckConflicts method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCheckConflictsParams", + "text": "AuthorizeCheckConflictsParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeCheckConflictsParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "The objects to authorize" + ], + "signature": [ + "AuthorizeObject", + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeCreateObject", + "type": "Interface", + "tags": [], + "label": "AuthorizeCreateObject", + "description": [ + "\nThe AuthorizeCreateObject interface extends AuthorizeObjectWithExistingSpaces\nand contains an array of initial namespaces for the object. Used by\nthe authorizeCreate and authorizeBulkCreate methods." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCreateObject", + "text": "AuthorizeCreateObject" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeCreateObject.initialNamespaces", + "type": "Array", + "tags": [], + "label": "initialNamespaces", + "description": [ + "\nInitial spaces to include the created object. Populated by options\npassed to the repository's bulkCreate method." + ], + "signature": [ + "string[] | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeCreateParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeCreateParams", + "description": [ + "\nThe AuthorizeCreateParams interface extends AuthorizeParams and is\nused for the AuthorizeCreate method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCreateParams", + "text": "AuthorizeCreateParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeCreateParams.object", + "type": "Object", + "tags": [], + "label": "object", + "description": [ + "The object to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCreateObject", + "text": "AuthorizeCreateObject" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeDeleteParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeDeleteParams", + "description": [ + "\nThe AuthorizeDeleteParams interface extends AuthorizeParams and is\nused for the AuthorizeDelete method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeDeleteParams", + "text": "AuthorizeDeleteParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeDeleteParams.object", + "type": "Object", + "tags": [], + "label": "object", + "description": [ + "The object to authorize" + ], + "signature": [ + "AuthorizeObject" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeFindParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeFindParams", + "description": [ + "\nThe AuthorizeFindParams interface is used for the AuthorizeFind method\nof the ISavedObjectsSecurityExtension." + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeFindParams.namespaces", + "type": "Object", + "tags": [], + "label": "namespaces", + "description": [ + "The namespaces in which to find objects" + ], + "signature": [ + "Set" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeFindParams.types", + "type": "Object", + "tags": [], + "label": "types", + "description": [ + "The types of objects to find" + ], + "signature": [ + "Set" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeGetParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeGetParams", + "description": [ + "\nThe AuthorizeGetParams interface extends AuthorizeParams and is\nused for the AuthorizeGet method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeGetParams", + "text": "AuthorizeGetParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeGetParams.object", + "type": "Object", + "tags": [], + "label": "object", + "description": [ + "The object to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeGetParams.objectNotFound", + "type": "CompoundType", + "tags": [], + "label": "objectNotFound", + "description": [ + "Whether or not the object was not found, defaults to false" + ], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeObjectWithExistingSpaces", + "type": "Interface", + "tags": [], + "label": "AuthorizeObjectWithExistingSpaces", + "description": [ + "\nThe AuthorizeObjectWithExistingSpaces extends AuthorizeObject and contains\nan array of existing namespaces for the object. Used by the\nauthorizeDelete, authorizeBulkDelete, authorizeGet,\nauthorizeCheckConflicts, and getFindRedactTypeMap methods." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + }, + " extends ", + "AuthorizeObject" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeObjectWithExistingSpaces.existingNamespaces", + "type": "Array", + "tags": [], + "label": "existingNamespaces", + "description": [ + "\nSpaces where the object is known to exist. Usually populated\nby document data from the result of an es query." + ], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateObject", + "type": "Interface", + "tags": [], + "label": "AuthorizeUpdateObject", + "description": [ + "\nThe AuthorizeUpdateObject interface extends AuthorizeObjectWithExistingSpaces\nand contains a object namespace override. Used by the authorizeUpdate\nand authorizeBulkUpdate methods." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeUpdateObject", + "text": "AuthorizeUpdateObject" + }, + " extends ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateObject.objectNamespace", + "type": "string", + "tags": [], + "label": "objectNamespace", + "description": [ + "\nThe namespace in which to update this object. Populated by options\npassed to the repository's update or bulkUpdate method." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeUpdateParams", + "description": [ + "\nThe AuthorizeUpdateParams interface extends AuthorizeParams and is\nused for the AuthorizeUpdate method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeUpdateParams", + "text": "AuthorizeUpdateParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateParams.object", + "type": "Object", + "tags": [], + "label": "object", + "description": [ + "The object to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeUpdateObject", + "text": "AuthorizeUpdateObject" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateSpacesParams", + "type": "Interface", + "tags": [], + "label": "AuthorizeUpdateSpacesParams", + "description": [ + "\nThe AuthorizeUpdateSpacesParams interface extends AuthorizeParams and is\nused for the AuthorizeUpdateSpaces method of the ISavedObjectsSecurityExtension." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeUpdateSpacesParams", + "text": "AuthorizeUpdateSpacesParams" + }, + " extends ", + "AuthorizeParams" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateSpacesParams.spacesToAdd", + "type": "Array", + "tags": [], + "label": "spacesToAdd", + "description": [ + "The spaces in which to add the objects" + ], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateSpacesParams.spacesToRemove", + "type": "Array", + "tags": [], + "label": "spacesToRemove", + "description": [ + "The spaces from which to remove the objects" + ], + "signature": [ + "string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeUpdateSpacesParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "The objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.BulkResolveError", + "type": "Interface", + "tags": [], + "label": "BulkResolveError", + "description": [ + "\nError result for the internal bulk resolve method." + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.BulkResolveError.type", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "The type of the saved object" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.BulkResolveError.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The id of the saved object" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.BulkResolveError.error", + "type": "Object", + "tags": [], + "label": "error", + "description": [ + "The decorated resolve error" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.CheckAuthorizationResult", + "type": "Interface", + "tags": [], + "label": "CheckAuthorizationResult", + "description": [ + "\nThe CheckAuthorizationResult interface contains the overall status of an\nauthorization check and the specific authorized privileges as an\nAuthorizationTypeMap." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.CheckAuthorizationResult.status", + "type": "CompoundType", + "tags": [], + "label": "status", + "description": [ + "\nThe overall status of the authorization check as a string:\n'fully_authorized' | 'partially_authorized' | 'unauthorized'" + ], + "signature": [ + "\"unauthorized\" | \"fully_authorized\" | \"partially_authorized\"" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.CheckAuthorizationResult.typeMap", + "type": "Object", + "tags": [], + "label": "typeMap", + "description": [ + "\nThe specific authorized privileges: a map of type to record\nof action/AuthorizationTypeEntry (spaces/globallyAuthz'd)" + ], + "signature": [ + "Map>" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.DecoratedError", + "type": "Interface", + "tags": [], + "label": "DecoratedError", + "description": [ + "\nThe DecoratedError interface extends the Boom error object\nand augments it with the 'SavedObjectsClientErrorCode' symbol\nproperty." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.DecoratedError", + "text": "DecoratedError" + }, + " extends ", + "Boom", + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.DecoratedError.code", + "type": "string", + "tags": [], + "label": "[code]", + "description": [ + "the 'SavedObjectsClientErrorCode' symbol" + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.EncryptedObjectDescriptor", + "type": "Interface", + "tags": [], + "label": "EncryptedObjectDescriptor", + "description": [ + "\nThe EncryptedObjectDescriptor interface contains settings for describing\nan object to be encrypted or decrpyted." + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.EncryptedObjectDescriptor.type", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "The Saved Object type" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.EncryptedObjectDescriptor.id", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The Saved Object ID" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.EncryptedObjectDescriptor.namespace", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "Namespace for use in index migration...\nIf the object is being decrypted during index migration, the object was previously\nencrypted with its namespace in the descriptor portion of the AAD; on the other hand,\nif the object is being decrypted during object migration, the object was never encrypted\nwith its namespace in the descriptor portion of the AAD." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.GetFindRedactTypeMapParams", + "type": "Interface", + "tags": [], + "label": "GetFindRedactTypeMapParams", + "description": [ + "\nThe GetFindRedactTypeMapParams interface is used for the GetFindRedactTypeMap\nmethod of the ISavedObjectsSecurityExtension." + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.GetFindRedactTypeMapParams.previouslyCheckedNamespaces", + "type": "Object", + "tags": [], + "label": "previouslyCheckedNamespaces", + "description": [ + "The namespaces previously checked by the AuthorizeFind method" + ], + "signature": [ + "Set" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.GetFindRedactTypeMapParams.objects", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "\nThe objects to authorize in order to generate the type map\nthis should be populated by the result of the es query" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeObjectWithExistingSpaces", + "text": "AuthorizeObjectWithExistingSpaces" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension", + "type": "Interface", + "tags": [], + "label": "ISavedObjectsEncryptionExtension", + "description": [ + "\nThe ISavedObjectsEncryptionExtension interface defines the functions of a saved objects\nrepository encryption extension. It contains functions for determining if a type is\nencryptable, encrypting object attributes, and decrypting or stripping object attributes." + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.isEncryptableType", + "type": "Function", + "tags": [], + "label": "isEncryptableType", + "description": [ + "\nReturns true if a type has been registered as encryptable." + ], + "signature": [ + "(type: string) => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.isEncryptableType.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "- the string name of the object type" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "boolean, true if type is encryptable" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.decryptOrStripResponseAttributes", + "type": "Function", + "tags": [], + "label": "decryptOrStripResponseAttributes", + "description": [ + "\nGiven a saved object, will return a decrypted saved object or will strip\nattributes from the returned object if decryption fails." + ], + "signature": [ + ">(response: R, originalAttributes?: T | undefined) => Promise" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.decryptOrStripResponseAttributes.$1", + "type": "Uncategorized", + "tags": [], + "label": "response", + "description": [ + "- any object R that extends SavedObject with attributes T" + ], + "signature": [ + "R" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.decryptOrStripResponseAttributes.$2", + "type": "Uncategorized", + "tags": [], + "label": "originalAttributes", + "description": [ + "- optional, original attributes T from when the object was created (NOT encrypted).\nThese are used to avoid decryption execution cost if they are supplied." + ], + "signature": [ + "T | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [ + "R with decrypted or stripped attributes" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.encryptAttributes", + "type": "Function", + "tags": [], + "label": "encryptAttributes", + "description": [ + "\nGiven a saved object descriptor and some attributes, returns an encrypted version\nof supplied attributes." + ], + "signature": [ + ">(descriptor: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.EncryptedObjectDescriptor", + "text": "EncryptedObjectDescriptor" + }, + ", attributes: T) => Promise" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.encryptAttributes.$1", + "type": "Object", + "tags": [], + "label": "descriptor", + "description": [ + "- an object containing a saved object id, type, and optional namespace." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.EncryptedObjectDescriptor", + "text": "EncryptedObjectDescriptor" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsEncryptionExtension.encryptAttributes.$2", + "type": "Uncategorized", + "tags": [], + "label": "attributes", + "description": [ + "- T, attributes of the specified object, some of which to be encrypted." + ], + "signature": [ + "T" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "T, encrypted attributes" + ] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsExporter", "type": "Interface", "tags": [], - "label": "CheckAuthorizationParams", + "label": "ISavedObjectsExporter", "description": [ - "\nThe CheckAuthorizationParams interface contains settings for checking\nauthorization via the ISavedObjectsSecurityExtension." - ], - "signature": [ - { - "pluginId": "@kbn/core-saved-objects-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.CheckAuthorizationParams", - "text": "CheckAuthorizationParams" - }, - "" + "\nUtility class used to export savedObjects.\n" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationParams.types", - "type": "Object", - "tags": [], - "label": "types", + "id": "def-common.ISavedObjectsExporter.exportByTypes", + "type": "Function", + "tags": [ + "throws" + ], + "label": "exportByTypes", "description": [ - "\nA set of types to check." + "\nGenerates an export stream for given types.\n\nSee the {@link SavedObjectsExportByTypeOptions | options} for more detailed information.\n" ], "signature": [ - "Set" + "(options: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsExportByTypeOptions", + "text": "SavedObjectsExportByTypeOptions" + }, + ") => Promise<", + "Readable", + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsExporter.exportByTypes.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsExportByTypeOptions", + "text": "SavedObjectsExportByTypeOptions" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationParams.spaces", - "type": "Object", - "tags": [], - "label": "spaces", + "id": "def-common.ISavedObjectsExporter.exportByObjects", + "type": "Function", + "tags": [ + "throws" + ], + "label": "exportByObjects", "description": [ - "\nA set of spaces to check." + "\nGenerates an export stream for given object references.\n\nSee the {@link SavedObjectsExportByObjectOptions | options} for more detailed information.\n" ], "signature": [ - "Set" + "(options: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsExportByObjectOptions", + "text": "SavedObjectsExportByObjectOptions" + }, + ") => Promise<", + "Readable", + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", "deprecated": false, - "trackAdoption": false - }, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsExporter.exportByObjects.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsExportByObjectOptions", + "text": "SavedObjectsExportByObjectOptions" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsImporter", + "type": "Interface", + "tags": [], + "label": "ISavedObjectsImporter", + "description": [ + "\nUtility class used to import savedObjects.\n" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationParams.actions", - "type": "Object", - "tags": [], - "label": "actions", + "id": "def-common.ISavedObjectsImporter.import", + "type": "Function", + "tags": [ + "throws" + ], + "label": "import", "description": [ - "\nAn set of actions to check." + "\nImport saved objects from given stream. See the {@link SavedObjectsImportOptions | options} for more\ndetailed information.\n" ], "signature": [ - "Set" + "(options: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsImportOptions", + "text": "SavedObjectsImportOptions" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectsImportResponse", + "text": "SavedObjectsImportResponse" + }, + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsImporter.import.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsImportOptions", + "text": "SavedObjectsImportOptions" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationParams.options", - "type": "Object", - "tags": [], - "label": "options", + "id": "def-common.ISavedObjectsImporter.resolveImportErrors", + "type": "Function", + "tags": [ + "throws" + ], + "label": "resolveImportErrors", "description": [ - "\nAuthorization options - whether or not to allow global resources, false if options are undefined" + "\nResolve and return saved object import errors.\nSee the {@link SavedObjectsResolveImportErrorsOptions | options} for more detailed information.\n" ], "signature": [ - "{ allowGlobalResource: boolean; } | undefined" + "(options: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsResolveImportErrorsOptions", + "text": "SavedObjectsResolveImportErrorsOptions" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectsImportResponse", + "text": "SavedObjectsImportResponse" + }, + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsImporter.resolveImportErrors.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsResolveImportErrorsOptions", + "text": "SavedObjectsResolveImportErrorsOptions" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationResult", - "type": "Interface", - "tags": [], - "label": "CheckAuthorizationResult", - "description": [ - "\nThe CheckAuthorizationResult interface contains the overall status of an\nauthorization check and the specific authorized privileges as an\nAuthorizationTypeMap." - ], - "signature": [ - { - "pluginId": "@kbn/core-saved-objects-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.CheckAuthorizationResult", - "text": "CheckAuthorizationResult" - }, - "" + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension", + "type": "Interface", + "tags": [], + "label": "ISavedObjectsSecurityExtension", + "description": [ + "\nThe ISavedObjectsSecurityExtension interface defines the functions of a saved objects repository security extension.\nIt contains functions for checking & enforcing authorization, adding audit events, and redacting namespaces." ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -301,195 +3430,391 @@ "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationResult.status", - "type": "CompoundType", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeCreate", + "type": "Function", "tags": [], - "label": "status", + "label": "authorizeCreate", "description": [ - "\nThe overall status of the authorization check as a string:\n'fully_authorized' | 'partially_authorized' | 'unauthorized'" + "\nPerforms authorization for the CREATE security action" ], "signature": [ - "\"unauthorized\" | \"fully_authorized\" | \"partially_authorized\"" + "(params: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCreateParams", + "text": "AuthorizeCreateParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeCreate.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "the namespace and object to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCreateParams", + "text": "AuthorizeCreateParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.CheckAuthorizationResult.typeMap", - "type": "Object", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkCreate", + "type": "Function", "tags": [], - "label": "typeMap", + "label": "authorizeBulkCreate", "description": [ - "\nThe specific authorized privileges: a map of type to record\nof action/AuthorizationTypeEntry (spaces/globallyAuthz'd)" + "\nPerforms authorization for the BULK_CREATE security action" ], "signature": [ - "Map(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.AuthorizationTypeEntry", - "text": "AuthorizationTypeEntry" + "section": "def-common.AuthorizeBulkCreateParams", + "text": "AuthorizeBulkCreateParams" }, - ">>" + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EncryptedObjectDescriptor", - "type": "Interface", - "tags": [], - "label": "EncryptedObjectDescriptor", - "description": [ - "\nThe EncryptedObjectDescriptor interface contains settings for describing\nan object to be encrypted or decrpyted." - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EncryptedObjectDescriptor.type", - "type": "string", - "tags": [], - "label": "type", - "description": [ - "The Saved Object type" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EncryptedObjectDescriptor.id", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "The Saved Object ID" + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkCreate.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "the namespace and objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkCreateParams", + "text": "AuthorizeBulkCreateParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", - "deprecated": false, - "trackAdoption": false + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EncryptedObjectDescriptor.namespace", - "type": "string", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeUpdate", + "type": "Function", "tags": [], - "label": "namespace", + "label": "authorizeUpdate", "description": [ - "Namespace for use in index migration...\nIf the object is being decrypted during index migration, the object was previously\nencrypted with its namespace in the descriptor portion of the AAD; on the other hand,\nif the object is being decrypted during object migration, the object was never encrypted\nwith its namespace in the descriptor portion of the AAD." + "\nPerforms authorization for the UPDATE security action" ], "signature": [ - "string | undefined" + "(params: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeUpdateParams", + "text": "AuthorizeUpdateParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EnforceAuthorizationParams", - "type": "Interface", - "tags": [], - "label": "EnforceAuthorizationParams", - "description": [ - "\nThe EnforceAuthorizationParams interface contains settings for\nenforcing a single action via the ISavedObjectsSecurityExtension." - ], - "signature": [ - { - "pluginId": "@kbn/core-saved-objects-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.EnforceAuthorizationParams", - "text": "EnforceAuthorizationParams" + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeUpdate.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "the namespace and object to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeUpdateParams", + "text": "AuthorizeUpdateParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] }, - "" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EnforceAuthorizationParams.typesAndSpaces", - "type": "Object", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkUpdate", + "type": "Function", "tags": [], - "label": "typesAndSpaces", + "label": "authorizeBulkUpdate", "description": [ - "\nA map of types to spaces that will be affected by the action" + "\nPerforms authorization for the BULK_UPDATE security action" ], "signature": [ - "Map>" + "(params: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkUpdateParams", + "text": "AuthorizeBulkUpdateParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkUpdate.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "the namespace and objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkUpdateParams", + "text": "AuthorizeBulkUpdateParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EnforceAuthorizationParams.action", - "type": "Uncategorized", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeDelete", + "type": "Function", "tags": [], - "label": "action", + "label": "authorizeDelete", "description": [ - "\nThe relevant action (create, update, etc.)" + "\nPerforms authorization for the DELETE security action" ], "signature": [ - "A" + "(params: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeDeleteParams", + "text": "AuthorizeDeleteParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeDelete.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "the namespace and object to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeDeleteParams", + "text": "AuthorizeDeleteParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EnforceAuthorizationParams.typeMap", - "type": "Object", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkDelete", + "type": "Function", "tags": [], - "label": "typeMap", + "label": "authorizeBulkDelete", "description": [ - "\nThe authorization map from CheckAuthorizationResult: a\nmap of type to record of action/AuthorizationTypeEntry\n(spaces/globallyAuthz'd)" + "\nPerforms authorization for the BULK_DELETE security action" ], "signature": [ - "Map(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.AuthorizationTypeEntry", - "text": "AuthorizationTypeEntry" + "section": "def-common.AuthorizeBulkDeleteParams", + "text": "AuthorizeBulkDeleteParams" }, - ">>" + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkDelete.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [ + "the namespace and objects to authorize" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkDeleteParams", + "text": "AuthorizeBulkDeleteParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EnforceAuthorizationParams.auditCallback", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeGet", "type": "Function", "tags": [], - "label": "auditCallback", + "label": "authorizeGet", "description": [ - "\nA callback intended to handle adding audit events in\nboth error (unauthorized), or success (authorized)\ncases" + "\nPerforms authorization for the GET security action" ], "signature": [ - "((error?: Error | undefined) => void) | undefined" + "(params: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeGetParams", + "text": "AuthorizeGetParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -497,503 +3822,566 @@ "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.EnforceAuthorizationParams.auditCallback.$1", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeGet.$1", "type": "Object", "tags": [], - "label": "error", - "description": [], + "label": "params", + "description": [ + "the namespace, object to authorize, and whether or not the object was found" + ], "signature": [ - "Error | undefined" + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeGetParams", + "text": "AuthorizeGetParams" + } ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, - "isRequired": false + "isRequired": true } ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension", - "type": "Interface", - "tags": [], - "label": "ISavedObjectsEncryptionExtension", - "description": [ - "\nThe ISavedObjectsEncryptionExtension interface defines the functions of a saved objects\nrepository encryption extension. It contains functions for determining if a type is\nencryptable, encrypting object attributes, and decrypting or stripping object attributes." - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] + }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.isEncryptableType", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkGet", "type": "Function", "tags": [], - "label": "isEncryptableType", + "label": "authorizeBulkGet", "description": [ - "\nReturns true if a type has been registered as encryptable." + "\nPerforms authorization for the BULK_GET security action" ], "signature": [ - "(type: string) => boolean" + "(params: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkGetParams", + "text": "AuthorizeBulkGetParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.isEncryptableType.$1", - "type": "string", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeBulkGet.$1", + "type": "Object", "tags": [], - "label": "type", + "label": "params", "description": [ - "- the string name of the object type" + "the namespace and objects to authorize" ], "signature": [ - "string" + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeBulkGetParams", + "text": "AuthorizeBulkGetParams" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "isRequired": true } ], "returnComment": [ - "boolean, true if type is encryptable" + "CheckAuthorizationResult - the resulting authorization level and authorization map" ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.decryptOrStripResponseAttributes", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeCheckConflicts", "type": "Function", "tags": [], - "label": "decryptOrStripResponseAttributes", + "label": "authorizeCheckConflicts", "description": [ - "\nGiven a saved object, will return a decrypted saved object or will strip\nattributes from the returned object if decryption fails." + "\nPerforms authorization for the CHECK_CONFLICTS security action" ], "signature": [ - "(params: ", { - "pluginId": "@kbn/core-saved-objects-common", + "pluginId": "@kbn/core-saved-objects-server", "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCheckConflictsParams", + "text": "AuthorizeCheckConflictsParams" }, - ">(response: R, originalAttributes?: T | undefined) => Promise" + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.decryptOrStripResponseAttributes.$1", - "type": "Uncategorized", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeCheckConflicts.$1", + "type": "Object", "tags": [], - "label": "response", + "label": "params", "description": [ - "- any object R that extends SavedObject with attributes T" + "the namespace and objects to authorize" ], "signature": [ - "R" + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeCheckConflictsParams", + "text": "AuthorizeCheckConflictsParams" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "isRequired": true + } + ], + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeRemoveReferences", + "type": "Function", + "tags": [], + "label": "authorizeRemoveReferences", + "description": [ + "\nPerforms authorization for the REMOVE_REFERENCES security action" + ], + "signature": [ + "(params: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeDeleteParams", + "text": "AuthorizeDeleteParams" + }, + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" }, + ">" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.decryptOrStripResponseAttributes.$2", - "type": "Uncategorized", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeRemoveReferences.$1", + "type": "Object", "tags": [], - "label": "originalAttributes", + "label": "params", "description": [ - "- optional, original attributes T from when the object was created (NOT encrypted).\nThese are used to avoid decryption execution cost if they are supplied." + "the namespace and object to authorize" ], "signature": [ - "T | undefined" + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeDeleteParams", + "text": "AuthorizeDeleteParams" + } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, - "isRequired": false + "isRequired": true } ], "returnComment": [ - "R with decrypted or stripped attributes" + "CheckAuthorizationResult - the resulting authorization level and authorization map" ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.encryptAttributes", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeOpenPointInTime", "type": "Function", "tags": [], - "label": "encryptAttributes", + "label": "authorizeOpenPointInTime", "description": [ - "\nGiven a saved object descriptor and some attributes, returns an encrypted version\nof supplied attributes." + "\nPerforms authorization for the OPEN_POINT_IN_TIME security action" ], "signature": [ - ">(descriptor: ", + "(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.EncryptedObjectDescriptor", - "text": "EncryptedObjectDescriptor" + "section": "def-common.AuthorizeFindParams", + "text": "AuthorizeFindParams" }, - ", attributes: T) => Promise" + ") => Promise<", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" + }, + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.encryptAttributes.$1", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeOpenPointInTime.$1", "type": "Object", "tags": [], - "label": "descriptor", + "label": "params", "description": [ - "- an object containing a saved object id, type, and optional namespace." + "the namespaces and types to authorize" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.EncryptedObjectDescriptor", - "text": "EncryptedObjectDescriptor" + "section": "def-common.AuthorizeFindParams", + "text": "AuthorizeFindParams" } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsEncryptionExtension.encryptAttributes.$2", - "type": "Uncategorized", - "tags": [], - "label": "attributes", - "description": [ - "- T, attributes of the specified object, some of which to be encrypted." - ], - "signature": [ - "T" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/encryption.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "isRequired": true } ], "returnComment": [ - "T, encrypted attributes" + "CheckAuthorizationResult - the resulting authorization level and authorization map" ] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsExporter", - "type": "Interface", - "tags": [], - "label": "ISavedObjectsExporter", - "description": [ - "\nUtility class used to export savedObjects.\n" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsExporter.exportByTypes", + "id": "def-common.ISavedObjectsSecurityExtension.auditClosePointInTime", "type": "Function", - "tags": [ - "throws" + "tags": [], + "label": "auditClosePointInTime", + "description": [ + "\nPerforms audit logging for the CLOSE_POINT_IN_TIME security action" ], - "label": "exportByTypes", + "signature": [ + "() => void" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeAndRedactMultiNamespaceReferences", + "type": "Function", + "tags": [], + "label": "authorizeAndRedactMultiNamespaceReferences", "description": [ - "\nGenerates an export stream for given types.\n\nSee the {@link SavedObjectsExportByTypeOptions | options} for more detailed information.\n" + "\nHandles all security operations for the COLLECT_MULTINAMESPACE_REFERENCES security action\nChecks/enforces authorization, writes audit events, filters the object graph, and redacts spaces from the share_to_space/bulk_get\nresponse. In other SavedObjectsRepository functions we do this before decrypting attributes. However, because of the\nshare_to_space/bulk_get response logic involved in deciding between the exact match or alias match, it's cleaner to do authorization,\nauditing, filtering, and redaction all afterwards." ], "signature": [ - "(options: ", + "(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsExportByTypeOptions", - "text": "SavedObjectsExportByTypeOptions" + "section": "def-common.AuthorizeAndRedactMultiNamespaceReferencesParams", + "text": "AuthorizeAndRedactMultiNamespaceReferencesParams" }, ") => Promise<", - "Readable", - ">" + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectReferenceWithContext", + "text": "SavedObjectReferenceWithContext" + }, + "[]>" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsExporter.exportByTypes.$1", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeAndRedactMultiNamespaceReferences.$1", "type": "Object", "tags": [], - "label": "options", - "description": [], + "label": "params", + "description": [ + "- the namespace, objects to authorize, and purpose of the operation" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsExportByTypeOptions", - "text": "SavedObjectsExportByTypeOptions" + "section": "def-common.AuthorizeAndRedactMultiNamespaceReferencesParams", + "text": "AuthorizeAndRedactMultiNamespaceReferencesParams" } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "SavedObjectReferenceWithContext[] - array of collected references" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsExporter.exportByObjects", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeAndRedactInternalBulkResolve", "type": "Function", - "tags": [ - "throws" - ], - "label": "exportByObjects", + "tags": [], + "label": "authorizeAndRedactInternalBulkResolve", "description": [ - "\nGenerates an export stream for given object references.\n\nSee the {@link SavedObjectsExportByObjectOptions | options} for more detailed information.\n" + "\nHandles all security operations for the INTERNAL_BULK_RESOLVE security action\nChecks authorization, writes audit events, and redacts namespaces from the bulkResolve response. In other SavedObjectsRepository\nfunctions we do this before decrypting attributes. However, because of the bulkResolve logic involved in deciding between the exact match\nor alias match, it's cleaner to do authorization, auditing, and redaction all afterwards." ], "signature": [ - "(options: ", + "(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsExportByObjectOptions", - "text": "SavedObjectsExportByObjectOptions" + "section": "def-common.AuthorizeAndRedactInternalBulkResolveParams", + "text": "AuthorizeAndRedactInternalBulkResolveParams" }, - ") => Promise<", - "Readable", - ">" + ") => Promise<(", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsResolveResponse", + "text": "SavedObjectsResolveResponse" + }, + " | ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.BulkResolveError", + "text": "BulkResolveError" + }, + ")[]>" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsExporter.exportByObjects.$1", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeAndRedactInternalBulkResolve.$1", "type": "Object", "tags": [], - "label": "options", - "description": [], + "label": "params", + "description": [ + "- the namespace and objects to authorize" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsExportByObjectOptions", - "text": "SavedObjectsExportByObjectOptions" - } + "section": "def-common.AuthorizeAndRedactInternalBulkResolveParams", + "text": "AuthorizeAndRedactInternalBulkResolveParams" + }, + "" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/export.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "isRequired": true } ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsImporter", - "type": "Interface", - "tags": [], - "label": "ISavedObjectsImporter", - "description": [ - "\nUtility class used to import savedObjects.\n" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + "returnComment": [ + "Array of SavedObjectsResolveResponses or BulkResolveErrors - the redacted resolve responses or errors" + ] + }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsImporter.import", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeUpdateSpaces", "type": "Function", - "tags": [ - "throws" - ], - "label": "import", + "tags": [], + "label": "authorizeUpdateSpaces", "description": [ - "\nImport saved objects from given stream. See the {@link SavedObjectsImportOptions | options} for more\ndetailed information.\n" + "\nPerforms authorization for the UPDATE_OBJECTS_SPACES security action" ], "signature": [ - "(options: ", + "(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsImportOptions", - "text": "SavedObjectsImportOptions" + "section": "def-common.AuthorizeUpdateSpacesParams", + "text": "AuthorizeUpdateSpacesParams" }, ") => Promise<", { - "pluginId": "@kbn/core-saved-objects-common", + "pluginId": "@kbn/core-saved-objects-server", "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObjectsImportResponse", - "text": "SavedObjectsImportResponse" + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" }, - ">" + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsImporter.import.$1", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeUpdateSpaces.$1", "type": "Object", "tags": [], - "label": "options", - "description": [], + "label": "params", + "description": [ + "- namespace, spacesToAdd, spacesToRemove, and objects to authorize" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsImportOptions", - "text": "SavedObjectsImportOptions" + "section": "def-common.AuthorizeUpdateSpacesParams", + "text": "AuthorizeUpdateSpacesParams" } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsImporter.resolveImportErrors", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeFind", "type": "Function", - "tags": [ - "throws" - ], - "label": "resolveImportErrors", + "tags": [], + "label": "authorizeFind", "description": [ - "\nResolve and return saved object import errors.\nSee the {@link SavedObjectsResolveImportErrorsOptions | options} for more detailed information.\n" + "\nPerforms authorization for the FIND security action\nThis method is the first of two security steps for the find operation (saved objects repository's find method)\nThis method should be called first in order to provide data needed to construct the type-to-namespace map for the search DSL" ], "signature": [ - "(options: ", + "(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsResolveImportErrorsOptions", - "text": "SavedObjectsResolveImportErrorsOptions" + "section": "def-common.AuthorizeFindParams", + "text": "AuthorizeFindParams" }, ") => Promise<", { - "pluginId": "@kbn/core-saved-objects-common", + "pluginId": "@kbn/core-saved-objects-server", "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObjectsImportResponse", - "text": "SavedObjectsImportResponse" + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.CheckAuthorizationResult", + "text": "CheckAuthorizationResult" }, - ">" + ">" ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsImporter.resolveImportErrors.$1", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeFind.$1", "type": "Object", "tags": [], - "label": "options", - "description": [], + "label": "params", + "description": [ + "- namespaces and types to authorize" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.SavedObjectsResolveImportErrorsOptions", - "text": "SavedObjectsResolveImportErrorsOptions" + "section": "def-common.AuthorizeFindParams", + "text": "AuthorizeFindParams" } ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/import.ts", + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, "trackAdoption": false, "isRequired": true } ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension", - "type": "Interface", - "tags": [], - "label": "ISavedObjectsSecurityExtension", - "description": [ - "\nThe ISavedObjectsSecurityExtension interface defines the functions of a saved objects repository security extension.\nIt contains functions for checking & enforcing authorization, adding audit events, and redacting namespaces." - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + "returnComment": [ + "CheckAuthorizationResult - the resulting authorization level and authorization map" + ] + }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.performAuthorization", + "id": "def-common.ISavedObjectsSecurityExtension.getFindRedactTypeMap", "type": "Function", "tags": [], - "label": "performAuthorization", + "label": "getFindRedactTypeMap", "description": [ - "\nPerforms authorization (check & enforce) of actions on specified types in specified spaces." + "\nGets an updated type map for redacting results of the FIND security action\nThis method is the second of two security steps for the find operation (saved objects repository's find method)\nThis method should be called last in order to update the type map used to redact namespaces in the results" ], "signature": [ - "(params: ", + "(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.PerformAuthorizationParams", - "text": "PerformAuthorizationParams" + "section": "def-common.GetFindRedactTypeMapParams", + "text": "GetFindRedactTypeMapParams" }, - ") => Promise<", + ") => Promise<", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.CheckAuthorizationResult", - "text": "CheckAuthorizationResult" + "section": "def-common.AuthorizationTypeMap", + "text": "AuthorizationTypeMap" }, - ">" + " | undefined>" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1001,22 +4389,21 @@ "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.performAuthorization.$1", + "id": "def-common.ISavedObjectsSecurityExtension.getFindRedactTypeMap.$1", "type": "Object", "tags": [], "label": "params", "description": [ - "- actions, types & spaces map, audit callback, options (enforce bypassed if enforce map is undefined)" + "- namespace, spacesToAdd, spacesToRemove, and objects to authorize" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.PerformAuthorizationParams", - "text": "PerformAuthorizationParams" - }, - "" + "section": "def-common.GetFindRedactTypeMapParams", + "text": "GetFindRedactTypeMapParams" + } ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1025,28 +4412,36 @@ } ], "returnComment": [ - "CheckAuthorizationResult - the resulting authorization level and authorization map" + "- the updated type map used for redaction" ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.enforceAuthorization", + "id": "def-common.ISavedObjectsSecurityExtension.redactNamespaces", "type": "Function", "tags": [], - "label": "enforceAuthorization", + "label": "redactNamespaces", "description": [ - "\nEnforces authorization of a single action on specified types in specified spaces.\nThrows error if authorization map does not cover specified parameters." + "\nFilters a saved object's spaces based on an authorization map (from CheckAuthorizationResult)" ], "signature": [ - "(params: ", + "(params: ", { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.EnforceAuthorizationParams", - "text": "EnforceAuthorizationParams" + "section": "def-common.RedactNamespacesParams", + "text": "RedactNamespacesParams" + }, + ") => ", + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObject", + "text": "SavedObject" }, - ") => void" + "" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1054,22 +4449,22 @@ "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.enforceAuthorization.$1", + "id": "def-common.ISavedObjectsSecurityExtension.redactNamespaces.$1", "type": "Object", "tags": [], "label": "params", "description": [ - "- map of types/spaces, action to check, and authz map (from CheckAuthorizationResult)" + "- the saved object and an authorization map" ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", "scope": "common", "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.EnforceAuthorizationParams", - "text": "EnforceAuthorizationParams" + "section": "def-common.RedactNamespacesParams", + "text": "RedactNamespacesParams" }, - "" + "" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1077,27 +4472,29 @@ "isRequired": true } ], - "returnComment": [] + "returnComment": [ + "SavedObject - saved object with filtered spaces" + ] }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.addAuditEvent", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeDisableLegacyUrlAliases", "type": "Function", "tags": [], - "label": "addAuditEvent", + "label": "authorizeDisableLegacyUrlAliases", "description": [ - "\nAdds an audit event for the specified action with relevant information" + "\nPerforms authorization for the disableLegacyUrlAliases method of the SecureSpacesClientWrapper\nThere is no return for this method. If unauthorized the method with throw, otherwise will resolve." ], "signature": [ - "(params: ", + "(aliases: ", { - "pluginId": "@kbn/core-saved-objects-server", + "pluginId": "@kbn/core-saved-objects-common", "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.AddAuditEventParams", - "text": "AddAuditEventParams" + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.LegacyUrlAliasTarget", + "text": "LegacyUrlAliasTarget" }, - ") => void" + "[]) => void" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1105,21 +4502,22 @@ "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.addAuditEvent.$1", - "type": "Object", + "id": "def-common.ISavedObjectsSecurityExtension.authorizeDisableLegacyUrlAliases.$1", + "type": "Array", "tags": [], - "label": "params", + "label": "aliases", "description": [ - "- the action, outcome, error, and relevant object/space information" + "- array of legacy url alias targets" ], "signature": [ { - "pluginId": "@kbn/core-saved-objects-server", + "pluginId": "@kbn/core-saved-objects-common", "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.AddAuditEventParams", - "text": "AddAuditEventParams" - } + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.LegacyUrlAliasTarget", + "text": "LegacyUrlAliasTarget" + }, + "[]" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1131,31 +4529,23 @@ }, { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.redactNamespaces", + "id": "def-common.ISavedObjectsSecurityExtension.auditObjectsForSpaceDeletion", "type": "Function", "tags": [], - "label": "redactNamespaces", + "label": "auditObjectsForSpaceDeletion", "description": [ - "\nFilters a saved object's spaces based on an authorization map (from CheckAuthorizationResult)" + "\nPerforms saved object audit logging for the delete method of the SecureSpacesClientWrapper" ], "signature": [ - "(params: ", - { - "pluginId": "@kbn/core-saved-objects-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.RedactNamespacesParams", - "text": "RedactNamespacesParams" - }, - ") => ", + "(spaceId: string, objects: ", { - "pluginId": "@kbn/core-saved-objects-common", + "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsFindResult", + "text": "SavedObjectsFindResult" }, - "" + "[]) => void" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1163,22 +4553,39 @@ "children": [ { "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.ISavedObjectsSecurityExtension.redactNamespaces.$1", - "type": "Object", + "id": "def-common.ISavedObjectsSecurityExtension.auditObjectsForSpaceDeletion.$1", + "type": "string", "tags": [], - "label": "params", + "label": "spaceId", "description": [ - "- the saved object and an authorization map" + "- the id of the space being deleted" + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.ISavedObjectsSecurityExtension.auditObjectsForSpaceDeletion.$2", + "type": "Array", + "tags": [], + "label": "objects", + "description": [ + "- the objects to audit" ], "signature": [ { - "pluginId": "@kbn/core-saved-objects-server", + "pluginId": "@kbn/core-saved-objects-api-server", "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.RedactNamespacesParams", - "text": "RedactNamespacesParams" + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsFindResult", + "text": "SavedObjectsFindResult" }, - "" + "[]" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", "deprecated": false, @@ -1186,9 +4593,7 @@ "isRequired": true } ], - "returnComment": [ - "SavedObject - saved object with filtered spaces" - ] + "returnComment": [] } ], "initialIsOpen": false @@ -2097,146 +5502,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams", - "type": "Interface", - "tags": [], - "label": "PerformAuthorizationParams", - "description": [ - "\nThe PerformAuthorizationParams interface contains settings for checking\n& enforcing authorization via the ISavedObjectsSecurityExtension." - ], - "signature": [ - { - "pluginId": "@kbn/core-saved-objects-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsServerPluginApi", - "section": "def-common.PerformAuthorizationParams", - "text": "PerformAuthorizationParams" - }, - "" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams.actions", - "type": "Object", - "tags": [], - "label": "actions", - "description": [ - "\nA set of actions to check." - ], - "signature": [ - "Set" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams.types", - "type": "Object", - "tags": [], - "label": "types", - "description": [ - "\nA set of types to check." - ], - "signature": [ - "Set" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams.spaces", - "type": "Object", - "tags": [], - "label": "spaces", - "description": [ - "\nA set of spaces to check (types to check comes from the typesAndSpaces map)." - ], - "signature": [ - "Set" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams.enforceMap", - "type": "Object", - "tags": [], - "label": "enforceMap", - "description": [ - "\nA map of types (key) to spaces (value) that will be affected by the action(s).\nIf undefined, enforce with be bypassed." - ], - "signature": [ - "Map> | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams.auditCallback", - "type": "Function", - "tags": [], - "label": "auditCallback", - "description": [ - "\nA callback intended to handle adding audit events in\nboth error (unauthorized), or success (authorized)\ncases" - ], - "signature": [ - "((error?: Error | undefined) => void) | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams.auditCallback.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.PerformAuthorizationParams.options", - "type": "Object", - "tags": [], - "label": "options", - "description": [ - "\nAuthorization options\nallowGlobalResource - whether or not to allow global resources, false if options are undefined" - ], - "signature": [ - "{ allowGlobalResource: boolean; } | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/core-saved-objects-server", "id": "def-common.RedactNamespacesParams", @@ -2267,7 +5532,7 @@ "tags": [], "label": "savedObject", "description": [ - "\nrelevant saved object" + "Relevant saved object" ], "signature": [ { @@ -2316,7 +5581,9 @@ "type": "Interface", "tags": [], "label": "SavedObject", - "description": [], + "description": [ + "\nDefinition of the Saved Object interface\n" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-common", @@ -2568,6 +5835,10 @@ "plugin": "@kbn/core-saved-objects-common", "path": "packages/core/saved-objects/core-saved-objects-common/src/saved_objects.ts" }, + { + "plugin": "@kbn/core-saved-objects-api-server", + "path": "packages/core/saved-objects/core-saved-objects-api-server/index.ts" + }, { "plugin": "@kbn/core", "path": "src/core/server/index.ts" @@ -3153,7 +6424,7 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references: ", + "SavedObjectDoc & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -3161,7 +6432,7 @@ "section": "def-common.SavedObjectReference", "text": "SavedObjectReference" }, - "[]; }" + "[] | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts", "deprecated": false, @@ -3238,7 +6509,7 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references: ", + "SavedObjectDoc & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -3246,7 +6517,7 @@ "section": "def-common.SavedObjectReference", "text": "SavedObjectReference" }, - "[]; }" + "[] | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts", "deprecated": false, @@ -3359,7 +6630,7 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references: ", + "SavedObjectDoc & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -3367,7 +6638,7 @@ "section": "def-common.SavedObjectReference", "text": "SavedObjectReference" }, - "[]; }" + "[] | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts", "deprecated": false, @@ -3832,7 +7103,9 @@ "type": "Object", "tags": [], "label": "encryptionExtension", - "description": [], + "description": [ + "The encryption extension - handles encrypting and decrypting attributes of saved objects" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", @@ -3853,7 +7126,9 @@ "type": "Object", "tags": [], "label": "securityExtension", - "description": [], + "description": [ + "The security extension - handles action authorization, audit logging, and space redaction" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", @@ -3874,7 +7149,9 @@ "type": "Object", "tags": [], "label": "spacesExtension", - "description": [], + "description": [ + "The spaces extension - handles retrieving the current space and retrieving available spaces" + ], "signature": [ { "pluginId": "@kbn/core-saved-objects-server", @@ -6188,6 +9465,10 @@ "plugin": "@kbn/core-saved-objects-migration-server-internal", "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts" }, + { + "plugin": "@kbn/core-saved-objects-migration-server-internal", + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migrations.ts" + }, { "plugin": "actions", "path": "x-pack/plugins/actions/server/saved_objects/index.ts" @@ -6358,23 +9639,35 @@ }, { "plugin": "@kbn/core-saved-objects-migration-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" }, { "plugin": "@kbn/core-saved-objects-migration-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" }, { "plugin": "@kbn/core-saved-objects-migration-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" }, { "plugin": "@kbn/core-saved-objects-migration-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" }, { "plugin": "@kbn/core-saved-objects-migration-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/document_migrator.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" + }, + { + "plugin": "@kbn/core-saved-objects-migration-server-internal", + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" + }, + { + "plugin": "@kbn/core-saved-objects-migration-server-internal", + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" + }, + { + "plugin": "@kbn/core-saved-objects-migration-server-internal", + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/document_migrator/validate_migration.test.ts" } ] }, @@ -6869,22 +10162,7 @@ "initialIsOpen": false } ], - "enums": [ - { - "parentPluginId": "@kbn/core-saved-objects-server", - "id": "def-common.AuditAction", - "type": "Enum", - "tags": [], - "label": "AuditAction", - "description": [ - "\nThe AuditAction enumeration contains values for all\nvalid audit actions for use in AddAuditEventParams." - ], - "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - } - ], + "enums": [], "misc": [ { "parentPluginId": "@kbn/core-saved-objects-server", @@ -6911,6 +10189,29 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.AuthorizeOpenPointInTimeParams", + "type": "Type", + "tags": [], + "label": "AuthorizeOpenPointInTimeParams", + "description": [ + "\nThe AuthorizeOpenPointInTimeParams interface is used for the\nAuthorizeOpenPointInTime method of the ISavedObjectsSecurityExtension.\nIt is identical to AuthorizeFindParams." + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.AuthorizeFindParams", + "text": "AuthorizeFindParams" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/extensions/security.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-server", "id": "def-common.CreatedObject", @@ -7101,7 +10402,7 @@ "\nDocument type used during model migration.\n" ], "signature": [ - "SavedObjectDoc & { references: ", + "SavedObjectDoc & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -7109,7 +10410,7 @@ "section": "def-common.SavedObjectReference", "text": "SavedObjectReference" }, - "[]; }" + "[] | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts", "deprecated": false, @@ -7165,7 +10466,7 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references: ", + "SavedObjectDoc & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -7173,7 +10474,7 @@ "section": "def-common.SavedObjectReference", "text": "SavedObjectReference" }, - "[]; }" + "[] | undefined; }" ], "path": "packages/core/saved-objects/core-saved-objects-server/src/model_version/transformations.ts", "deprecated": false, diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index a403c4063b8dd..48022a5f10927 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; @@ -21,16 +21,16 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 358 | 0 | 104 | 1 | +| 489 | 1 | 98 | 4 | ## Common +### Classes + + ### Interfaces -### Enums - - ### Consts, variables and types diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 7e24ee03e181a..77b2f3e60023e 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index fe825c9f8e57d..c85ff0bd026e4 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.devdocs.json b/api_docs/kbn_core_saved_objects_utils_server.devdocs.json index a3e686f6bdabc..6410d1a27e5ea 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_utils_server.devdocs.json @@ -18,1503 +18,6 @@ }, "common": { "classes": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers", - "type": "Class", - "tags": [], - "label": "SavedObjectsErrorHelpers", - "description": [], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isSavedObjectsClientError", - "type": "Function", - "tags": [], - "label": "isSavedObjectsClientError", - "description": [], - "signature": [ - "(error: any) => error is ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isSavedObjectsClientError.$1", - "type": "Any", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "any" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateBadRequestError", - "type": "Function", - "tags": [], - "label": "decorateBadRequestError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateBadRequestError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateBadRequestError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createBadRequestError", - "type": "Function", - "tags": [], - "label": "createBadRequestError", - "description": [], - "signature": [ - "(reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createBadRequestError.$1", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createUnsupportedTypeError", - "type": "Function", - "tags": [], - "label": "createUnsupportedTypeError", - "description": [], - "signature": [ - "(type: string) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createUnsupportedTypeError.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isBadRequestError", - "type": "Function", - "tags": [], - "label": "isBadRequestError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isBadRequestError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createInvalidVersionError", - "type": "Function", - "tags": [], - "label": "createInvalidVersionError", - "description": [], - "signature": [ - "(versionInput?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createInvalidVersionError.$1", - "type": "string", - "tags": [], - "label": "versionInput", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isInvalidVersionError", - "type": "Function", - "tags": [], - "label": "isInvalidVersionError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isInvalidVersionError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateNotAuthorizedError", - "type": "Function", - "tags": [], - "label": "decorateNotAuthorizedError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isNotAuthorizedError", - "type": "Function", - "tags": [], - "label": "isNotAuthorizedError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isNotAuthorizedError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateForbiddenError", - "type": "Function", - "tags": [], - "label": "decorateForbiddenError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateForbiddenError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateForbiddenError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isForbiddenError", - "type": "Function", - "tags": [], - "label": "isForbiddenError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isForbiddenError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError", - "type": "Function", - "tags": [], - "label": "decorateRequestEntityTooLargeError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isRequestEntityTooLargeError", - "type": "Function", - "tags": [], - "label": "isRequestEntityTooLargeError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isRequestEntityTooLargeError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundError", - "type": "Function", - "tags": [], - "label": "createGenericNotFoundError", - "description": [], - "signature": [ - "(type?: string | null, id?: string | null) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundError.$1", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string | null" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundError.$2", - "type": "CompoundType", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string | null" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createIndexAliasNotFoundError", - "type": "Function", - "tags": [], - "label": "createIndexAliasNotFoundError", - "description": [], - "signature": [ - "(alias: string) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createIndexAliasNotFoundError.$1", - "type": "string", - "tags": [], - "label": "alias", - "description": [], - "signature": [ - "string" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError", - "type": "Function", - "tags": [], - "label": "decorateIndexAliasNotFoundError", - "description": [], - "signature": [ - "(error: Error, alias: string) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$2", - "type": "string", - "tags": [], - "label": "alias", - "description": [], - "signature": [ - "string" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isNotFoundError", - "type": "Function", - "tags": [], - "label": "isNotFoundError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isNotFoundError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateConflictError", - "type": "Function", - "tags": [], - "label": "decorateConflictError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateConflictError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateConflictError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createConflictError", - "type": "Function", - "tags": [], - "label": "createConflictError", - "description": [], - "signature": [ - "(type: string, id: string, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createConflictError.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createConflictError.$2", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createConflictError.$3", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isConflictError", - "type": "Function", - "tags": [], - "label": "isConflictError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isConflictError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateTooManyRequestsError", - "type": "Function", - "tags": [], - "label": "decorateTooManyRequestsError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createTooManyRequestsError", - "type": "Function", - "tags": [], - "label": "createTooManyRequestsError", - "description": [], - "signature": [ - "(type: string, id: string) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createTooManyRequestsError.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createTooManyRequestsError.$2", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isTooManyRequestsError", - "type": "Function", - "tags": [], - "label": "isTooManyRequestsError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isTooManyRequestsError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError", - "type": "Function", - "tags": [], - "label": "decorateEsCannotExecuteScriptError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError", - "type": "Function", - "tags": [], - "label": "isEsCannotExecuteScriptError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateEsUnavailableError", - "type": "Function", - "tags": [], - "label": "decorateEsUnavailableError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateEsUnavailableError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateEsUnavailableError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isEsUnavailableError", - "type": "Function", - "tags": [], - "label": "isEsUnavailableError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isEsUnavailableError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateGeneralError", - "type": "Function", - "tags": [], - "label": "decorateGeneralError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateGeneralError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.decorateGeneralError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isGeneralError", - "type": "Function", - "tags": [], - "label": "isGeneralError", - "description": [], - "signature": [ - "(error: Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - ") => boolean" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.isGeneralError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError", - "type": "Function", - "tags": [], - "label": "createGenericNotFoundEsUnavailableError", - "description": [], - "signature": [ - "(type?: string | null, id?: string | null) => ", - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - } - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$1", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string | null" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$2", - "type": "CompoundType", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string | null" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/core-saved-objects-utils-server", "id": "def-common.SavedObjectsUtils", @@ -2047,48 +550,7 @@ "initialIsOpen": false } ], - "interfaces": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.DecoratedError", - "type": "Interface", - "tags": [], - "label": "DecoratedError", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-saved-objects-utils-server", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", - "section": "def-common.DecoratedError", - "text": "DecoratedError" - }, - " extends ", - "Boom", - "" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-utils-server", - "id": "def-common.DecoratedError.code", - "type": "string", - "tags": [], - "label": "[code]", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - } - ], + "interfaces": [], "enums": [], "misc": [ { diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 3ff41c8bb431e..759fcc31b19dc 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 108 | 1 | 84 | 0 | +| 30 | 0 | 6 | 0 | ## Common @@ -31,9 +31,6 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core ### Classes -### Interfaces - - ### Consts, variables and types diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 31cfab5c61d04..f766dbf91f4ed 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index b601a33e53576..a32de019c776b 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index e8126372b6ace..1438b8b6b16db 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 3fedc80803bc0..f033238f49811 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 5dae78dad9483..61067f029fc72 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 1e3561cbfd7f9..0fc6c866a1a8a 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index ff87fa48218c5..2120cc7ec4efc 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 56b03e898f379..9458a3945d4c7 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index 7e7e78cd9f7e9..65aab7d20315a 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 9ce8895db1951..310aa92948b17 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index c30df7720fbbf..035f88057b053 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 30b4a093dbe76..d06104243dc19 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index ad298a9ba263b..5fbcd1be9692a 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index 4e536bf592e9b..a83b90b2748f7 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 0f4dc1002f2d9..5aba39e5b836e 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 6e89a84a54937..c5c4cf61ca2a6 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index b7ab28b96d25a..ca30857413d36 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 31c4ed741972c..9c9484b7afee9 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index d4acf2400ece7..c01ad7d6eb83b 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index f92df43e8d060..e2a042fd22137 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index f5e20259805dc..6c65a8c267647 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 84c6e45dd8e40..1ed33d3489b68 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 0957d6890a06c..97cf81dd6cf19 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 1d7e886a05c31..414ef07a41bb8 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 26510907ea7e4..93230a78f1ae1 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 45d6b5dcf39a8..775e7af2567b6 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 67e6b158633e8..9a50676c8844e 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 551962ac51eab..c968e5a148c34 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 44a4be1a6b8d3..38771fed3d0cb 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 4b4596e6fb78a..13e93db276218 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 598d9411b5614..8b774dbe02cd8 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 512d5d8a00cfb..b4f574a854eea 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 16ca3f10a1103..208971474ba68 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 8b7c5bf0f6f5d..9560edf50e575 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 82f63dd8b51b6..cbcddb5c26572 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 94d5063295087..2379c28b2290d 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 1e3e074184a47..ae60f75644b93 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 3138075a95e99..195d55a5fefcd 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 73a2e26b3b259..cfd05a2910055 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index c9b50f5ebd279..d96ebe475c3a8 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 28fb0ac8704ab..85e37890b4f81 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 6663eda11a6e0..f1e05ee91a52a 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 77c8716abd0de..46f49d6f3a440 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index e230c19572827..7b1c5c2782229 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 324050b0ad6c4..99459b2724e4c 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index d6abcaa9d690e..b62d5eae0ceac 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 302cc3e9b10ee..3046a4c6ca139 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 903fb558a7042..d076ef6209022 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index dd3c794d44669..d18719c1a1c52 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 632c05aeb57ab..c7caf7e1381c4 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 67f0e587885f6..b3bd82323024e 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 71c0accd6e1cb..46a7f6f0fd6d9 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 45fd4e6ef9d17..49d48495b2a09 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 7e12ce50a74df..7de7fb437791c 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index a3896f6a0b3d3..8408f5eaecc80 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 51d7c684fc6ec..822131c4358c6 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index db7487a91573f..80860d0973c1e 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 81b632adb7ac5..42bf5e30df173 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 1f66c3d7549d6..208d175598ba2 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 5d44834a937fe..ca4312aa608d6 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index c60069347e57c..2355effaa4338 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index 3bb12ce02adc0..db40623912faa 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index fecca34e01c36..206189b5817ea 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index bbb17c8ccd11d..d0c35339b2859 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 52f523d494559..0bc1e9c2a083f 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index add9664a67bce..67d80d955891f 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 967b937409f7b..94823871e3f66 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 04862f7979fdb..5155e41df91ac 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index f6e6ad8257019..044d9fe5c5d5d 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 40d30b11a31ee..7bc8836029023 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index 4274ea4d6eff2..d9dfb7bbdc992 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index fae81f825444b..c2e5309373671 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 4e000300ae661..1b16e64a1b48f 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 1d63be1cdd0e9..510c572fe1d48 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index a13f026414f05..0d531d2730049 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 536cb804c5e12..ff84137fdd118 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 7306be2c9d762..c9639f1afba8c 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index 72b827c703e8f..33f62f4a23306 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 7b9e100531ed2..9bc705ebc0cf2 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index be7721b10a4c0..57c232ed410bc 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 9420a1be31c96..6c4b9093948ad 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index 9f593e23c763f..eee4f24cb91f0 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index d7407bdc18e74..2c07170eaf526 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index f8d695112f16c..36a3dcdd58e81 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index ee76efa7a85b2..d636dc99094dc 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index 65c7c9b5799d5..2a373c86eb76f 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 2e93d17dfb69f..2b12d25b3fede 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index fb545e779afc8..ac42537054b41 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index bad67ea825f90..a4afd817ac65f 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 38dc0b06dc377..020034226e817 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index a60c7c538c232..5fa83b8867d0b 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 442c7e227c39c..20c071e13c510 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 096b617ece933..fd52fef164cba 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 048815782437e..f38890ce0c0b0 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 193abc5239773..524ad7930f21d 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index a1651573d7b80..dc368dc60e47b 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 0bc66bdfebaa1..ef2c040d9d90c 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 78896922f34c2..62474c089a86e 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index fec6980f5c24d..6377a0b56da96 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 790dd555f268b..6561acaa7e1e3 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index dc6be4b9aaa00..6eee665ab2421 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 5d75d41201329..d917da592a381 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 2259f81115eeb..9e377e1046d91 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 0f7570057526e..e3c1636d2583e 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 2193376962a09..aa69ead0f2a80 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index f056ee38cc2c2..2bf1023538138 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index f9b2fc6da7a37..8052a0c477ce2 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index db567980e7dc8..7ec8ecebf808e 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index f033970b0554f..748523e2b6dec 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 04a2752d2792f..a54eda9059921 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 5eae425140fa5..b53d63937bad5 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index f7d3c6b0d097c..90aad6717ab26 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 12e9b299ff2f7..448f01ef24cb1 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index a10976f8672ff..c112d796888b7 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index bce8fbd34e294..e0775860046a7 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index f38c9456cf908..184df4d0dfb4a 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 50e7d344eee9e..cb15f3227f596 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 38f0159f3dc25..2eec94a8e6cf0 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 6fc9a3c4a0471..1e37bcb30956a 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 25b29ad17b594..9d4ae20f6a40d 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 262d110956e28..89aeea6934702 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index bfcf855921336..63ac5a36f3d39 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 5509046249d4e..334c1cd62f96d 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index f9a1b148ac22a..c4db69beafd2c 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 4616cc85b649d..2fae876b53379 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 401a9c4d892a2..128ea8e8df9ef 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 331e2a8219f99..8f7b75ddf4c75 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 3e3c22e8cea8c..c5b2c4f5789fa 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index c46f162719f5c..da29f12b22595 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 240b44cc647f8..a7e4c290cbeda 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 2052b445c1f88..c84b324f13ed5 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index a4a60812a9aae..623f8e5f6072f 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 7c10100349b36..237333dead873 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index a12b99eceae7a..3e9711497a8a2 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 38683a38e5dcf..774024740a0a1 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index 72ec5b900df8d..01d3b6085c13e 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 3f1f82faa3d70..5a1d052961b55 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index e806085500d83..a7277b6729192 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 781695cd7c150..804f45a24e334 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 547f8c5924326..ecc7eda56ae9f 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 4bee6b82d0520..26fc8e4dd82b4 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 27dd36948e16e..2bc966fafaaea 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index d5db8841a23e2..8d66ebbd7ab5d 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 3e2c296064b71..4a63da3314288 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index c648824268cd7..4437e5bd930fb 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.devdocs.json b/api_docs/kbn_slo_schema.devdocs.json index b62b90bafb2a2..a368fed9d859c 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -390,7 +390,7 @@ "label": "CreateSLOInput", "description": [], "signature": [ - "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; } & { settings?: { timestampField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }" + "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; } & { settings?: { timestampField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -405,7 +405,7 @@ "label": "CreateSLOParams", "description": [], "signature": [ - "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: ", + "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: ", { "pluginId": "@kbn/slo-schema", "scope": "common", @@ -520,7 +520,7 @@ "label": "FindSLOResponse", "description": [], "signature": [ - "{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }" + "{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -535,7 +535,7 @@ "label": "GetSLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -580,7 +580,7 @@ "label": "SLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -595,7 +595,7 @@ "label": "SLOWithSummaryResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -610,7 +610,7 @@ "label": "UpdateSLOInput", "description": [], "signature": [ - "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; } | undefined; timeWindow?: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; } | undefined; budgetingMethod?: \"occurrences\" | \"timeslices\" | undefined; objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }) | undefined; settings?: { timestampField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }" + "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; } | undefined; timeWindow?: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; } | undefined; budgetingMethod?: \"occurrences\" | \"timeslices\" | undefined; objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }) | undefined; settings?: { timestampField?: string | undefined; syncDelay?: string | undefined; frequency?: string | undefined; } | undefined; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -625,7 +625,7 @@ "label": "UpdateSLOParams", "description": [], "signature": [ - "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; } | undefined; timeWindow?: { duration: ", + "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; } | undefined; timeWindow?: { duration: ", { "pluginId": "@kbn/slo-schema", "scope": "common", @@ -680,7 +680,7 @@ "label": "UpdateSLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }" ], "path": "packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -748,12 +748,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>" ], "path": "packages/kbn-slo-schema/src/schema/indicators.ts", @@ -832,6 +834,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>" ], "path": "packages/kbn-slo-schema/src/schema/indicators.ts", @@ -956,12 +960,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -1010,6 +1016,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -1390,12 +1398,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -1444,6 +1454,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -1652,12 +1664,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -1706,6 +1720,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -1954,12 +1970,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -2008,6 +2026,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -2334,12 +2354,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -2388,6 +2410,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -2548,12 +2572,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -2602,6 +2628,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -2764,12 +2792,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -2818,6 +2848,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -3006,12 +3038,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -3060,6 +3094,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -3374,12 +3410,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -3428,6 +3466,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -3580,12 +3620,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -3634,6 +3676,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 11cd045fee767..506bf83435e55 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index c04881a836b53..8cfc9af4765fb 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index e6089dbfdf438..e7b44a0595f2b 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 3bfd24437a1e9..639033423f782 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index ac8b2708f76db..71fa66aa5cd72 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index ff5cf2064900b..f34f4e79b477c 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 48967ec72de97..7ad93b1efc641 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 7913dcbabe6d7..547a3531cc3ca 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 707bef04c9a84..4c08f55f95fac 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 7cf2d95fc8bd5..d53ba028587f8 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index bdbdf35693958..7cf648110f7bb 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 7ba131992518b..af6e4c619a775 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index c5dbd2a3b3835..c9c179c190b3d 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 86da4a18f64dd..86665a71a6590 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 7eaf205e1f08e..cb914af306315 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 6b7cd18100ac9..92ec8131e2e49 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 4effbffc1ded8..73effc933ae1c 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 3417f1588657e..8151013e56478 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index b70a81183e7a5..32dc2d89e7ebb 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 90289f5f2ca61..526c731c0dd80 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 8648f681c07a4..537a58309875b 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json index 910974c5da2df..2ed468ecd418c 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -1364,6 +1364,18 @@ "plugin": "apm", "path": "x-pack/plugins/apm/public/components/routing/app_root.tsx" }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx" + }, + { + "plugin": "enterpriseSearch", + "path": "x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_view.tsx" + }, { "plugin": "indexLifecycleManagement", "path": "x-pack/plugins/index_lifecycle_management/public/shared_imports.ts" diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 42968ad83d606..34e2f7e1ee6d5 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 9df1daccf5420..73999ea92082c 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 6026a3aa815bf..a60441eb5d287 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index b4ed32e8be49a..2c8facb6f4740 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -484,6 +484,20 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Embeddable.badgeDomNode", + "type": "Object", + "tags": [], + "label": "badgeDomNode", + "description": [], + "signature": [ + "HTMLDivElement | undefined" + ], + "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "lens", "id": "def-public.Embeddable.handleEvent", diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index b7134b8874f7d..3292575c90c53 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 691 | 0 | 596 | 52 | +| 692 | 0 | 597 | 52 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index fa60cf1aa65fd..0a02ad7af5723 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 5af1695421960..29ec9594da556 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 10d0a63bbd959..1b015c35b488b 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 6909a97f3eb31..fe9acfdd27fde 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index e1e2156307051..98da0947c88ee 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 330794cfc8b5b..6ebdfb4285516 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index d7dae44b59d74..ff1f9c17c2136 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index a934616ae795e..d670b1b24d7f6 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 891b45992f8d0..a09fb315e1d15 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 81e6dcaf2ec5f..979e298e20962 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 69eb4dd1c5b8f..2580638a9ee21 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 29577ff1a56ab..bb76a13d3448b 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 51c41f0c06154..7f5e095373c87 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 6a5be5c8d32e6..777c50843ad4f 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -9849,12 +9849,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -9903,6 +9905,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -10007,7 +10011,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }, ", + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }, ", { "pluginId": "observability", "scope": "server", @@ -10037,7 +10041,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", { "pluginId": "observability", "scope": "server", @@ -10085,7 +10089,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", + ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", { "pluginId": "observability", "scope": "server", @@ -10267,12 +10271,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -10321,6 +10327,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -10563,12 +10571,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -10617,6 +10627,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -10721,7 +10733,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }, ", + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; }, ", { "pluginId": "observability", "scope": "server", @@ -10751,7 +10763,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", + ", { id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }, ", { "pluginId": "observability", "scope": "server", @@ -10799,7 +10811,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; 'threshold.us': number; } & { index?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", + ", { page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; } & { index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; } & { goodStatusCodes?: (\"2xx\" | \"3xx\" | \"4xx\" | \"5xx\")[] | undefined; index?: string | undefined; filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; filter: string; good: string; total: string; }; }; timeWindow: { duration: string; isRolling: boolean; } | { duration: string; calendar: { startTime: string; }; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { timestampField: string; syncDelay: string; frequency: string; }; enabled: boolean; createdAt: string; updatedAt: string; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }, ", { "pluginId": "observability", "scope": "server", @@ -10981,12 +10993,14 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; 'threshold.us': ", + "]>; threshold: ", "NumberC", "; }>, ", "PartialC", "<{ index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", @@ -11035,6 +11049,8 @@ "LiteralC", "<\"5xx\">]>>; index: ", "StringC", + "; filter: ", + "StringC", "; }>]>; }>, ", "TypeC", "<{ type: ", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 245841d7bb597..7a7ffa9c14ca1 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 4d3705ef44583..138fd3b29816f 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 57e9346a8b050..4c5a4a5c99f3d 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 67495 | 514 | 58491 | 1228 | +| 67630 | 515 | 58461 | 1230 | ## Plugin Directory @@ -30,9 +30,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 256 | 8 | 251 | 24 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 1 | 32 | 2 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 12 | 0 | 1 | 2 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 488 | 1 | 477 | 39 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 489 | 1 | 478 | 39 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 42 | 0 | 42 | 65 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 89 | 1 | 74 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 87 | 0 | 71 | 28 | @@ -47,7 +47,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | cloudLinks | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Adds the links to the Elastic Cloud console | 0 | 0 | 0 | 0 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | The cloud security posture plugin | 17 | 0 | 2 | 2 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 13 | 0 | 13 | 1 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 25 | 0 | 25 | 3 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 42 | 0 | 42 | 3 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 270 | 0 | 266 | 9 | | crossClusterReplication | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | customBranding | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Enables customization of Kibana | 0 | 0 | 0 | 0 | @@ -92,9 +92,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 1 | 2 | 0 | | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1077 | 3 | 972 | 26 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | -| globalSearchBar | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | -| globalSearchProviders | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 68 | 0 | 14 | 5 | +| globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | +| globalSearchProviders | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | | graph | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 0 | 0 | 0 | 0 | | grokdebugger | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Guided onboarding framework | 56 | 0 | 55 | 0 | @@ -112,7 +112,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | kibanaUsageCollection | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 609 | 3 | 416 | 9 | | | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 3 | 0 | 3 | 1 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 691 | 0 | 596 | 52 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 692 | 0 | 597 | 52 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 8 | 0 | 8 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | @@ -151,7 +151,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 7 | 0 | 7 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 118 | 0 | 59 | 10 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 22 | 1 | 22 | 1 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 261 | 0 | 65 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 253 | 0 | 65 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 12 | 0 | 12 | 2 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 4 | 0 | 4 | 0 | | synthetics | [@elastic/uptime](https://github.com/orgs/elastic/teams/uptime) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | @@ -213,7 +213,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 10 | 0 | 10 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 19 | 0 | 17 | 0 | -| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 20 | 0 | 15 | 3 | +| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 57 | 1 | 42 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 76 | 0 | 76 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 7 | 0 | 2 | 0 | @@ -249,8 +249,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 5 | 0 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 16 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 120 | 0 | 46 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 3 | 0 | 3 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 120 | 0 | 46 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 5 | 0 | 5 | 0 | @@ -343,23 +343,23 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 23 | 1 | 22 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 107 | 1 | 0 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 313 | 1 | 0 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 334 | 1 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 75 | 0 | 54 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 11 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 37 | 0 | 31 | 6 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 46 | 0 | 35 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 7 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 67 | 0 | 39 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 71 | 0 | 39 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 25 | 0 | 23 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 112 | 0 | 79 | 45 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 358 | 0 | 104 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 489 | 1 | 98 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 69 | 0 | 69 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 14 | 0 | 14 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 108 | 1 | 84 | 0 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 30 | 0 | 6 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 19 | 0 | 18 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 20 | 0 | 3 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 0e38c378bd105..f9b865ff8cbcf 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index f8b8b1ed2dada..e6001e7201715 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index a813156f67670..1548c3dfb2d53 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 6fedb5279e34f..d0a51aedf8ce5 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 2ce325cb45862..a21729e4639b7 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 7ef47eb4278e8..21a9aee8492cd 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index d3b73caf793a2..4d44041e9be5c 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 348d5b6fe2b8a..29d5b2a6ec11d 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index eef9a6675e682..8917557f4c7b0 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 809fc0aa6d21b..77a441c39dc96 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index f95b526b98dcb..a2340928add9e 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index e37fb538e907c..70ef0ecfd6823 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 0a843385af1dd..c2ebc14e8045f 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 65f7964263555..6deb948aaf417 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 4a5347bf950f7..f8dd9e4134c52 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 76a4ae3f2bd0e..481609084643f 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 8eefdbcd9d4df..7409186cd5c15 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 6b756882dee65..3ebbcad1a0d98 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index c5947e97e0789..f4cac153343a4 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index c2793b19d3ad7..9f39e867b3ac6 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.devdocs.json b/api_docs/spaces.devdocs.json index 43d132ba17352..97a2459425507 100644 --- a/api_docs/spaces.devdocs.json +++ b/api_docs/spaces.devdocs.json @@ -356,9 +356,9 @@ "signature": [ "(aliases: ", { - "pluginId": "spaces", + "pluginId": "@kbn/core-saved-objects-common", "scope": "common", - "docId": "kibSpacesPluginApi", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", "section": "def-common.LegacyUrlAliasTarget", "text": "LegacyUrlAliasTarget" }, @@ -377,9 +377,9 @@ "description": [], "signature": [ { - "pluginId": "spaces", + "pluginId": "@kbn/core-saved-objects-common", "scope": "common", - "docId": "kibSpacesPluginApi", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", "section": "def-common.LegacyUrlAliasTarget", "text": "LegacyUrlAliasTarget" }, @@ -3469,9 +3469,9 @@ "signature": [ "(aliases: ", { - "pluginId": "spaces", + "pluginId": "@kbn/core-saved-objects-common", "scope": "common", - "docId": "kibSpacesPluginApi", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", "section": "def-common.LegacyUrlAliasTarget", "text": "LegacyUrlAliasTarget" }, @@ -3492,9 +3492,9 @@ ], "signature": [ { - "pluginId": "spaces", + "pluginId": "@kbn/core-saved-objects-common", "scope": "common", - "docId": "kibSpacesPluginApi", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", "section": "def-common.LegacyUrlAliasTarget", "text": "LegacyUrlAliasTarget" }, @@ -3511,61 +3511,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "spaces", - "id": "def-server.LegacyUrlAliasTarget", - "type": "Interface", - "tags": [], - "label": "LegacyUrlAliasTarget", - "description": [ - "\nClient interface for interacting with legacy URL aliases." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "spaces", - "id": "def-server.LegacyUrlAliasTarget.targetSpace", - "type": "string", - "tags": [], - "label": "targetSpace", - "description": [ - "\nThe namespace that the object existed in when it was converted." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "spaces", - "id": "def-server.LegacyUrlAliasTarget.targetType", - "type": "string", - "tags": [], - "label": "targetType", - "description": [ - "\nThe type of the object when it was converted." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "spaces", - "id": "def-server.LegacyUrlAliasTarget.sourceId", - "type": "string", - "tags": [], - "label": "sourceId", - "description": [ - "\nThe original ID of the object, before it was converted." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "spaces", "id": "def-server.Space", @@ -4766,61 +4711,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "spaces", - "id": "def-common.LegacyUrlAliasTarget", - "type": "Interface", - "tags": [], - "label": "LegacyUrlAliasTarget", - "description": [ - "\nClient interface for interacting with legacy URL aliases." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "spaces", - "id": "def-common.LegacyUrlAliasTarget.targetSpace", - "type": "string", - "tags": [], - "label": "targetSpace", - "description": [ - "\nThe namespace that the object existed in when it was converted." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "spaces", - "id": "def-common.LegacyUrlAliasTarget.targetType", - "type": "string", - "tags": [], - "label": "targetType", - "description": [ - "\nThe type of the object when it was converted." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "spaces", - "id": "def-common.LegacyUrlAliasTarget.sourceId", - "type": "string", - "tags": [], - "label": "sourceId", - "description": [ - "\nThe original ID of the object, before it was converted." - ], - "path": "x-pack/plugins/spaces/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "spaces", "id": "def-common.Space", diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index a69d76fcf15de..899655b26377c 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 261 | 0 | 65 | 0 | +| 253 | 0 | 65 | 0 | ## Client diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 7944569d372c3..6be96495a6d33 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 6ebcc780605e2..fa6fed03d9ff2 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index f6d5542b2d7c5..21b5f8640f866 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index b565ff66d2396..690bf19a1c029 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 40dc09f5d1e9f..bb6944c6d589a 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index f0955006786af..d8fbe62929d29 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 4feb6ed0e54b7..5400f5c7eaeb1 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 0d553ba768315..4d86b62d3caca 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index b0aaada6656fd..9ccf2cc5d0c7c 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index 247ba5c81d7fd..6cede0d49e2ec 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index f0e3908b24feb..06cfebed3d937 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index bb5cd7e7f8c04..95084fa2aaccb 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 6065a7e6bc132..ce00787df3417 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 4304991da00a1..c3cd14977ac0b 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 927eede1455cc..6d456394e388c 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 80a68f5145aa8..313872400971c 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 1f79ae059c7c6..30b971c6f7ef3 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 310b3f35ddeeb..12c2643b3c707 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index e03b9d29c1012..6fd51629aa377 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 15a2cae052a90..2a714d235182c 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index 0e0422879a18d..d71de853a8536 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index a7a7371efc2c3..5e59b27ff0f42 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index dae2ee8b13a9a..1792dfa1faa54 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index b6dfb51ead1f5..e74951ff16de7 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 481bf669e83cf..4c9bbedccb11a 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index b7df3ec941be0..964b6dda69e49 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index dfef62440b354..b4619e59ea04b 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 59e4e6fef1e0d..2e13178b31acb 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 3a8944b4ab108..7dd061e5875ba 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 8837978d02e7f..cd9a8760c9197 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 44d7956893867..b30d9665b309e 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-02-15 +date: 2023-02-16 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From a7293f62b59508a4a2fa89fa04c278b70de5901f Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 16 Feb 2023 08:13:42 +0100 Subject: [PATCH 57/68] [Custom Branding] Add custom branding settings to Global settings (#150080) ## Summary This PR registers custom branding settings from the `custom_branding` plugin. Once registered, these settings can be viewed under "Global settings". UI changes: Screenshot 2023-02-06 at 19 59 19 I also removed the client-side version of the `custom_branding` plugin, as it's not needed. With this change, it became easier to test custom branding, so I made a few changes where logo was not showing properly or image size was wrong or test subjects were missing. I am working with @gchaps on the exact wording, so that might change. ### Checklist Delete any items that are not applicable to this PR. - [X] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [X] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [X] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [X] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [X] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) ~- [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~ - [X] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [X] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> --- .buildkite/ftr_configs.yml | 1 + .../src/ui/app_container.tsx | 1 + .../loading_indicator.test.tsx.snap | 1 + .../src/ui/header/header_logo.tsx | 7 +- .../src/ui/loading_indicator.tsx | 1 + .../src/rendering_service.tsx | 1 + packages/kbn-optimizer/limits.yml | 1 - test/functional/page_objects/settings_page.ts | 13 ++ x-pack/plugins/custom_branding/kibana.jsonc | 2 +- .../custom_branding/public/application.tsx | 23 --- .../custom_branding/public/components/app.tsx | 20 --- .../plugins/custom_branding/public/index.ts | 0 .../plugins/custom_branding/public/plugin.ts | 0 .../plugins/custom_branding/public/types.ts | 0 .../plugins/custom_branding/server/plugin.ts | 10 +- .../custom_branding/server/ui_settings.ts | 145 ++++++++++++++++++ x-pack/plugins/custom_branding/tsconfig.json | 3 + .../__snapshots__/login_page.test.tsx.snap | 6 + .../authentication/login/login_page.tsx | 6 +- x-pack/test/custom_branding/config.ts | 42 +++++ .../custom_branding/ftr_provider_context.ts | 13 ++ .../test/custom_branding/tests/acme_logo.png | Bin 0 -> 3243 bytes .../test/custom_branding/tests/acme_text.png | Bin 0 -> 2068 bytes x-pack/test/custom_branding/tests/index.ts | 14 ++ x-pack/test/custom_branding/tests/settings.ts | 100 ++++++++++++ 25 files changed, 358 insertions(+), 52 deletions(-) delete mode 100755 x-pack/plugins/custom_branding/public/application.tsx delete mode 100755 x-pack/plugins/custom_branding/public/components/app.tsx mode change 100755 => 100644 x-pack/plugins/custom_branding/public/index.ts mode change 100755 => 100644 x-pack/plugins/custom_branding/public/plugin.ts mode change 100755 => 100644 x-pack/plugins/custom_branding/public/types.ts create mode 100644 x-pack/plugins/custom_branding/server/ui_settings.ts create mode 100644 x-pack/test/custom_branding/config.ts create mode 100644 x-pack/test/custom_branding/ftr_provider_context.ts create mode 100644 x-pack/test/custom_branding/tests/acme_logo.png create mode 100644 x-pack/test/custom_branding/tests/acme_text.png create mode 100644 x-pack/test/custom_branding/tests/index.ts create mode 100644 x-pack/test/custom_branding/tests/settings.ts diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 62a42c4d4335e..4adb920c81e43 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -359,3 +359,4 @@ enabled: - x-pack/performance/journeys/ecommerce_dashboard_saved_search_only.ts - x-pack/performance/journeys/ecommerce_dashboard_tsvb_gauge_only.ts - x-pack/performance/journeys/dashboard_listing_page.ts + - x-pack/test/custom_branding/config.ts diff --git a/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx b/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx index 11899a938da9f..997185e05119c 100644 --- a/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx +++ b/packages/core/application/core-application-browser-internal/src/ui/app_container.tsx @@ -129,6 +129,7 @@ const AppLoadingPlaceholder: FC<{ showPlainSpinner: boolean }> = ({ showPlainSpi return ( diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx index 8b98e52f61c42..36a6663bf03f7 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_logo.tsx @@ -104,7 +104,12 @@ export function HeaderLogo({ href, navigateToApp, loadingCount$, ...observables > {customizedLogo ? ( - custom mark + custom mark ) : ( )} diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx index 13084f9500e58..8e3cae33ceb84 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/loading_indicator.tsx @@ -71,6 +71,7 @@ export class LoadingIndicator extends React.Component { - ReactDOM.render( - , - element - ); - - return () => ReactDOM.unmountComponentAtNode(element); -}; diff --git a/x-pack/plugins/custom_branding/public/components/app.tsx b/x-pack/plugins/custom_branding/public/components/app.tsx deleted file mode 100755 index 212584dc5ff38..0000000000000 --- a/x-pack/plugins/custom_branding/public/components/app.tsx +++ /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 React from 'react'; - -import { CoreStart } from '@kbn/core/public'; - -interface CustomBrandingAppDeps { - basename: string; - notifications: CoreStart['notifications']; - http: CoreStart['http']; -} - -export const CustomBrandingApp = (props: CustomBrandingAppDeps) => { - return
Hello
; -}; diff --git a/x-pack/plugins/custom_branding/public/index.ts b/x-pack/plugins/custom_branding/public/index.ts old mode 100755 new mode 100644 diff --git a/x-pack/plugins/custom_branding/public/plugin.ts b/x-pack/plugins/custom_branding/public/plugin.ts old mode 100755 new mode 100644 diff --git a/x-pack/plugins/custom_branding/public/types.ts b/x-pack/plugins/custom_branding/public/types.ts old mode 100755 new mode 100644 diff --git a/x-pack/plugins/custom_branding/server/plugin.ts b/x-pack/plugins/custom_branding/server/plugin.ts index 1c6039ace92df..958289bd9d14d 100755 --- a/x-pack/plugins/custom_branding/server/plugin.ts +++ b/x-pack/plugins/custom_branding/server/plugin.ts @@ -20,9 +20,8 @@ import { License } from '@kbn/license-api-guard-plugin/server'; import { CustomBranding } from '@kbn/core-custom-branding-common'; import { Subscription } from 'rxjs'; import { PLUGIN } from '../common/constants'; -import type { CustomBrandingRequestHandlerContext } from './types'; import { Dependencies } from './types'; -import { registerRoutes } from './routes'; +import { registerUiSettings } from './ui_settings'; const settingsKeys: Array = [ 'logo', @@ -49,8 +48,8 @@ export class CustomBrandingPlugin implements Plugin { pluginName: PLUGIN.getI18nName(i18n), logger: this.logger, }); - const router = core.http.createRouter(); - registerRoutes(router); + + registerUiSettings(core); const fetchFn = async ( request: KibanaRequest, @@ -95,8 +94,9 @@ export class CustomBrandingPlugin implements Plugin { const branding: CustomBranding = {}; for (let i = 0; i < settingsKeys!.length; i++) { const key = settingsKeys[i]; - const fullKey = `customBranding:${key}`; + const fullKey = `xpackCustomBranding:${key}`; const value = await uiSettingsClient.get(fullKey); + this.logger.info(`Fetching custom branding key ${fullKey} with value ${value}`); if (value) { branding[key] = value; } diff --git a/x-pack/plugins/custom_branding/server/ui_settings.ts b/x-pack/plugins/custom_branding/server/ui_settings.ts new file mode 100644 index 0000000000000..23eca349e5827 --- /dev/null +++ b/x-pack/plugins/custom_branding/server/ui_settings.ts @@ -0,0 +1,145 @@ +/* + * 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-server'; +import { UiSettingsParams } from '@kbn/core-ui-settings-common'; +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +export const UI_SETTINGS_CUSTOM_LOGO = 'xpackCustomBranding:logo'; +export const UI_SETTINGS_CUSTOMIZED_LOGO = 'xpackCustomBranding:customizedLogo'; +export const UI_SETTINGS_PAGE_TITLE = 'xpackCustomBranding:pageTitle'; +export const UI_SETTINGS_FAVICON_PNG = 'xpackCustomBranding:faviconPNG'; +export const UI_SETTINGS_FAVICON_SVG = 'xpackCustomBranding:faviconSVG'; +export const PLUGIN_ID = 'Custom Branding'; + +const kbToBase64Length = (kb: number) => Math.floor((kb * 1024 * 8) / 6); +const maxLogoSizeInBase64 = kbToBase64Length(200); +const dataurlRegex = /^data:([a-z]+\/[a-z0-9-+.]+)(;[a-z-]+=[a-z0-9-]+)?(;([a-z0-9]+))?,/; +const imageTypes = ['image/svg+xml', 'image/jpeg', 'image/png', 'image/gif']; + +const isImageData = (str: string) => { + const matches = str.match(dataurlRegex); + + if (!matches) { + return false; + } + + const [, mimetype, , , encoding] = matches; + const imageTypeIndex = imageTypes.indexOf(mimetype); + if (imageTypeIndex < 0 || encoding !== 'base64') { + return false; + } + + return true; +}; +const validateLogoBase64String = (str: string) => { + if (typeof str !== 'string' || !isImageData(str)) { + return i18n.translate('xpack.customBranding.uiSettings.validate.customLogo.badFile', { + defaultMessage: `Sorry, that file will not work. Please try a different image file.`, + }); + } + if (str.length > maxLogoSizeInBase64) { + return i18n.translate('xpack.customBranding.uiSettings.validate.customLogo.tooLarge', { + defaultMessage: `Sorry, that file is too large. The image file must be less than 200 kilobytes.`, + }); + } +}; + +export const ImageSchema = schema.nullable(schema.string({ validate: validateLogoBase64String })); + +const subscriptionLink = ` +
+ ${i18n.translate('xpack.customBranding.settings.subscriptionRequiredLink.text', { + defaultMessage: 'Subscription required.', + })} + + `; +export function registerUiSettings(core: CoreSetup) { + core.uiSettings.registerGlobal({ + [UI_SETTINGS_CUSTOM_LOGO]: { + name: i18n.translate('xpack.customBranding.customLogoLabel', { + defaultMessage: 'Logo icon', + }), + value: null, + description: i18n.translate('xpack.customBranding.customLogoDescription', { + defaultMessage: `Replaces the Elastic logo. Logos look best when they are no larger than 128 x 128 pixels and have a transparent background. {subscriptionLink}`, + values: { subscriptionLink }, + }), + sensitive: true, + type: 'image', + order: 1, + requiresPageReload: true, + schema: ImageSchema, + category: [PLUGIN_ID], + }, + [UI_SETTINGS_CUSTOMIZED_LOGO]: { + name: i18n.translate('xpack.customBranding.customizedLogoLabel', { + defaultMessage: 'Organization name', + }), + value: null, + description: i18n.translate('xpack.customBranding.customizedLogoDescription', { + defaultMessage: `Replaces the Elastic text. Images look best when they are no larger than 200 x 84 pixels and have a transparent background. {subscriptionLink}`, + values: { subscriptionLink }, + }), + sensitive: true, + type: 'image', + order: 2, + requiresPageReload: true, + schema: ImageSchema, + category: [PLUGIN_ID], + }, + [UI_SETTINGS_PAGE_TITLE]: { + name: i18n.translate('xpack.customBranding.pageTitleLabel', { + defaultMessage: 'Page title', + }), + value: null, + description: i18n.translate('xpack.customBranding.pageTitleDescription', { + defaultMessage: `The text that appears on browser tabs. {subscriptionLink}`, + values: { subscriptionLink }, + }), + sensitive: true, + type: 'string', + order: 3, + requiresPageReload: true, + schema: schema.nullable(schema.string()), + category: [PLUGIN_ID], + }, + [UI_SETTINGS_FAVICON_SVG]: { + name: i18n.translate('xpack.customBranding.faviconSVGTitle', { + defaultMessage: 'Favicon (SVG)', + }), + value: null, + description: i18n.translate('xpack.customBranding.faviconSVGDescription', { + defaultMessage: `A link to an icon that will appear on browser tabs. Recommended size is 16 x 16 pixels. {subscriptionLink}`, + values: { subscriptionLink }, + }), + sensitive: true, + type: 'string', + order: 4, + requiresPageReload: true, + schema: schema.nullable(schema.string()), + category: [PLUGIN_ID], + }, + [UI_SETTINGS_FAVICON_PNG]: { + name: i18n.translate('xpack.customBranding.faviconPNGTitle', { + defaultMessage: 'Favicon (PNG)', + }), + value: null, + description: i18n.translate('xpack.customBranding.faviconPNGDescription', { + defaultMessage: `An icon for use in browsers that don’t support svg. {subscriptionLink}`, + values: { subscriptionLink }, + }), + sensitive: true, + type: 'string', + order: 5, + requiresPageReload: true, + schema: schema.nullable(schema.string()), + category: [PLUGIN_ID], + }, + } as Record>); +} diff --git a/x-pack/plugins/custom_branding/tsconfig.json b/x-pack/plugins/custom_branding/tsconfig.json index e4446c82c3bbd..ac146c78954fc 100644 --- a/x-pack/plugins/custom_branding/tsconfig.json +++ b/x-pack/plugins/custom_branding/tsconfig.json @@ -16,6 +16,9 @@ "@kbn/i18n", "@kbn/core-http-request-handler-context-server", "@kbn/core-custom-branding-common", + "@kbn/core-lifecycle-server", + "@kbn/core-ui-settings-common", + "@kbn/config-schema", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap index 3cd7e8a92cb93..f5012047b49a0 100644 --- a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap @@ -324,6 +324,7 @@ exports[`LoginPage page renders as expected 1`] = ` /> { ) : ( ); + // custom logo needs to be centered + const logoStyle = customLogo ? { padding: 0 } : {}; return (
- {logo} + + {logo} +

; +export { services, pageObjects }; diff --git a/x-pack/test/custom_branding/tests/acme_logo.png b/x-pack/test/custom_branding/tests/acme_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8ed078b8b614775ab0aebcb4d7731a9b5b242a GIT binary patch literal 3243 zcmeH}_ct2~8^C4rQY>97+4j;~|w8~|)U5IYB$lZ*Ql4=*3T zfZ!iOr-emC#l$5f&zzN#J|`o4UQS*?5u&85q6$@0*MMolwX}6~_4NO|U|?uuY+`D5 z(Hw!aKv|-#tZi)VE@AAk4miA{le5cZS2u#Yho_gfk1z3xpMOALP;kiA(6DRa5!WOC zii*Awb2BzBJ|Qvb@8nyzQ&Q8?GcvR8Was4G&AXRhKq@LO`KR=L*@N2s%vV= zl)A@Ho<6I8-q6_eqWR^kme#iR*Bw+^XBYiVcMqetuYcg};LyAG!^{sOqhsR}lOLz1 zXJ+T-KP@aSeg3k%vby&5+xPX2%^zFaJG(zwd%yM%4v&rrOAGH$NU(WZqY%eTK$imm zz=cI4%y8lo-#Zgx-*EsLKUh)J;BSXYHjv5BQK$8)V9L~R$XCWL{axQ(b>?9j-l=A_ z(4mmDsHC#rLYMqCShcb+VUezCLg{Ac3^^8`BfF7A;FG)fmG+K|#|ZKDt5Nk>w6>yVDF>(}>1DDo>mca3?6G${o?O}zJ%$=Sfjx$I6o6Bf2irIS~bzXF9m7)otqnLm6@c}olc_eq|KWrP$1WcVA?Uy*g4W5D+4T`Y^;ZF}g zM#`bS8qRyZQvdSNyWF4UV#634l*XZ&zgE9KDBv+))k}pLk~3Y&HN^l#Agqg|yiqm3jo;@t({41f^fPunu+>DAZixZ+U$0f;Ns#Q03^GmOPs?8^( z>QI*V06IMP7{Tw0e?Um~QQtfmBBXQe^So>0njwxu9unhX87S@s6Zzm{ zb`WgQtFa?9x@#{XwuFw6RPu*9_-X9Lpi8*3?mjt^3=9h{GAGHLi-jmJ1D!&151aqDx*NR;DxFJmJ!8?M9NKosW{Mp$Mj0lbNp0QZ85?RcmQT918T%zGrm z+on55U;apR;=F?{>>?B6*=947$d|W}S^w5S;nEDs5fi+Z#87|K_aqM%JQy< zu3}WFJJ>?SG?&Lt)mwn1-p~zH)t0m*c>t>s0e@`X0SP z9SB!Y16{?JSDF@SbJLxF>9E0dz|j`*Q9f3=?ws*8rjT-6>y}aqek&8)tSnumO8s6m z6g8qsgK>lj(Wk%I=f>;Lh7Gn=39Uwh?4xV zisbk|_CEH(fJG4V--|_Vbr$Xl)U*!emZJ7XbA?EdZhsR<974y=^GOILk>NVWka*M> z)!6Un;mBj5;MCB9)l)LoDf;EL9lSJG{CG7wj^Bv3Q%l-DcD0RP2XA@t}tB2HK0cSh63&8 zgKD4_PSO8NSaayO8H@i8wPp88xxNLl1b84E$)%6=i1%ts%$qs}K(Q^NCTzdP`vmh& zBM=)|iw)J`u6^&OOQaEZNbD@WrIh9zI3YRxzdYFHdVuEo$Z~ zMjGGxbT14aEk+f_57%%_-KY4r3M6yVe}XB3B{?A-=J;L1Y~mx8GoQOiq7_yto<9b| zl`xGV&iqCRLAQd{QWEChGzZ1J6r8+Z*gl-RS~G=D&Rz9;euLi+dWmp^G{y^W%;lD} zsy7N!Mbz+(S3>QRH?(4LQRNTRp~--<5LW8SzTS`^ZPZb0xDTYiUH^)uE;WTCy=QwC zY?R!614uth7%`+7G?`63)$7(4aKPVjjcOzNY}fIF7;9f2cCi3YWn0T@gK0Akg@SRC zN>VjE{&8G%c>;6rwm1nvQKa+0s%k}J?stbRxu*hOpy}GE7vXeG8-vtTa)Wv%LEP{X zg(Uf`Cp>VqUbhpjvwqNJ3^kd3csatbG{@t|r?)elmll=@9}$Ie@YS=UAsM5e19R*9 zP{bOso;AOKf_jma*(b+ubx#h|JD%qz>bR~y3?9K zpN(=w8o!o{xGEot6`%$c;ExS)-^Ighv&l=4-1zR1Pf$ULoc4+^J2 z(K6=P^GE}pV%e!=>2?^ZL@vUC?R0*ZT*IG6d9AtX%Eihh=CfWOJd0L$*TcCBkd4RFM9s{FPIIBVE_OC literal 0 HcmV?d00001 diff --git a/x-pack/test/custom_branding/tests/acme_text.png b/x-pack/test/custom_branding/tests/acme_text.png new file mode 100644 index 0000000000000000000000000000000000000000..be1f51ec2550fee92ba5e63d47127fe47c83faf2 GIT binary patch literal 2068 zcmZWq2{_bwA0NghSFMZ`qr;HeVUGXbxx5%=%t5ll2nkz=TDjJh!H`0EY>E!oDiI2~ zLe3JixzBafTDe6duP8a%-%QzhpZE9tX6F0-T;K0J&;NP;v6dE8DRFsm1Og#Nr;(Z9 zHyV8QY(;`^qUCxC0wEk}X>Lsct>AE2ES8ax5e&mvEK6CL4r*Op77QQ8;Y_huBW-Ou z43h~2OLcWKES9RJWdbN7kq%fjH4{xu1`Jb)L<$~nj>FM4)l7kyNMsNQG$7DaH-lk1 z1W^eDAYkBdG#EC;sn9 z5m3N_$I~G|L7)XV6~dcBIKGPy;`xHldI%4wFZj105zlvldBM7ujoXW?)=s_|c2SzZ658^=qj9@o@4eWyHG$KF*f-fvc;2Sr^f{@Lj zRDqbE@YTzY62xvAH`~o*e!k!W<-aw5|IaQ#CcpkNm%kzZ3fixF0RlGv7U2i}zYKtz zd;Tl_pV41KK7IOBob9X)9>LyUnHE+EAz`G*cUwfaiit}|ZrlF7^bQ%6>`u8|yX6(o zihK6%+pmOCRym-05UZw+!xJD_Q(H&(M?L-j7#JFnOvt8G8r{tNu*FYID{Gd`QCo-O zj?O=yaB+1z~enH{GqT-U$vhs?`$5quewRQDRo;5acpEtL(wzYS3b@%l34-5{y zcscy{$g9z@@rlW)>6zEFb8mR_Zx`MzEx-TpadmBdW61uV2l#(NC(NkizOZUN83aPa znNB8I)3}W{R!po}DDL(+!rbm3Ze?fF8I>HR>@e@>@KC;PR#X)C%WMSG}MZNBVMUYq6)+Qa7qqqWxr0GSUK{s zke52&dN9+u$N0uh2MGy@9?zBEbzBbw2beWBuNwQ#O+($)1ITyz-OZg&TDx+R3^M!^ zvy_k9XQmEp;mmVwSA%wp^z2G=8StVs$9f1?piV2oY*zEF!R?5SV@&m)q{RrP#j}*= zDRZVp4b#Fjr!&;1k@aJ^Vo8!!)?MDS5wtq$plaRPeMgQ=_SI3vxSi4oua=hTggdWG zYZ6V#Om^sLDXI-mA6ehhWWwro*z(pc$#D2`vQEHaNE@PB7IjistthffRH*eHZX$Ac zd7=jK;lI5fQ+wuJ{yaPqiVdinMHaU`j8ZsP{QMR;he*1Aa{ktn_7f+Vm7TY!i!iH3 z+^3MAKK{^kBv>qG`EvQqF*13ma&p>p&|~ZhB^mSN)Yz2=Ee>}3m6S3c`p{C+FuaSC z-hcW2u~3$Var859Xy0ET-4y8NhM>l#nb>P^8+69q%lv3{jYQ0qhT=*g^ckd%K^OWO zPlbyUMHlFKqi!TBuc@MWx5KpDYFD?Rhc;Z}_1ES`bt;dd?RG}Jf49ILFVNZWZY|Q* zpcXDj!Nm5vI=NN~q9@aQ9WF^ee}Wq8)o4`@>`0hRJl{?DE8sitU)J>I^B)~Dx`v7G z7pk>8N|-G~%W(ESoxg|~<#;qC?dTIV_j{U<9bex^T2>j&Gl)y`smMR4)lum-64N3S zZH=s!eqb<;$?%&t9JLj*9FM~22L`_BAtgM{RR$I>#1*^wUWM zz4C#x9Dkv3DY;t`{a)o$Rn8@S>7~fI_)@6~C;ya?x7VXp(Rac#Y1q=J!45er7eeI8 zl*((W+UJWdp6&1%N)Bq@SHJK)%yS;jyUf0(=&$Uhpmx^fda3X+3}sIz8d-R$;^GW% z(Kn*Yg^SOR(_Fr=n5!h#wO=>qlFaViDoV_)^+s)yWx6u`;qDqGOLF9^6Vv5TI(tPp z7t*l@nn%akC-zDV;T%AyMqrq>_v($y*W@M#h!Rv+au53LNUo(I(bg+W$U)%`(Cg+ z4M<#iW8Hn_{+2b1OTo46yp3^myJJl%y { + const advancedSetting = await PageObjects.settings.getAdvancedSettings( + 'xpackCustomBranding:pageTitle' + ); + if (advancedSetting !== '') { + await PageObjects.settings.clearAdvancedSettings('xpackCustomBranding:pageTitle'); + } + try { + await find.byCssSelector('img[alt="xpackCustomBranding:logo"]'); + await PageObjects.settings.clearAdvancedSettings('xpackCustomBranding:logo'); + } catch (e) { + log.debug('It is expected not to find custom branding properties set'); + } + try { + await find.byCssSelector('img[alt="xpackCustomBranding:customizedLogo"]'); + await PageObjects.settings.clearAdvancedSettings('xpackCustomBranding:customizedLogo'); + } catch (e) { + log.debug('It is expected not to find custom branding properties set'); + } + }; + + const goToSettings = async () => { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaGlobalSettings(); + }; + + before(async function () { + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.uiSettings.replace({}); + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaGlobalSettings(); + // clear settings before tests start + await resetSettings(); + }); + + after(async function () { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaGlobalSettings(); + await resetSettings(); + }); + + beforeEach(async function () { + await goToSettings(); + }); + + it('should allow setting custom page title through advanced settings', async function () { + const pageTitle = 'Custom Page Title'; + const settingName = 'xpackCustomBranding:pageTitle'; + await PageObjects.settings.setAdvancedSettingsInput(settingName, pageTitle); + + const advancedSetting = await PageObjects.settings.getAdvancedSettings(settingName); + expect(advancedSetting).to.be(pageTitle); + }); + + it('should allow setting custom logo through advanced settings', async function () { + const settingName = 'xpackCustomBranding:logo'; + await PageObjects.settings.setAdvancedSettingsImage( + settingName, + require.resolve('./acme_logo.png') + ); + await goToSettings(); + const img = await find.byCssSelector('img[alt="logo"]'); + const imgSrc = await img.getAttribute('src'); + expect(imgSrc.startsWith('data:image/png')).to.be(true); + }); + + it('should allow setting custom logo text through advanced settings', async function () { + const settingName = 'xpackCustomBranding:customizedLogo'; + await PageObjects.settings.setAdvancedSettingsImage( + settingName, + require.resolve('./acme_text.png') + ); + await goToSettings(); + const logo = await testSubjects.find('logo'); + const img = await logo.findByCssSelector('.chrHeaderLogo__mark'); + const imgSrc = await img.getAttribute('src'); + expect(imgSrc.startsWith('data:image/png')).to.be(true); + }); + }); +} From b46d25a7ca700b7e4e01970c55dbc25357acf27a Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Thu, 16 Feb 2023 08:52:05 +0100 Subject: [PATCH 58/68] [ftr/journeys] allow override ftr base config in journey (#151277) ## Summary Currently journeys use pre-defined base FTR config and there is no way to re-configure Kibana to be loaded with extras, e.g. Fleet plugin configuration. https://github.com/elastic/kibana/blob/f443109eeae6af9873e39f16b53825941690c3be/packages/kbn-journeys/journey/journey_ftr_config.ts#L32-L34 Probably the easiest way to address it is to path custom FTR config directly in the journey. ``` export const journey = new Journey({ ftrConfigPath: 'x-pack/test/cloud_security_posture_api/config.ts', ... }) ``` It can be also considered as a step towards using journeys as future alternative to Webdriver functional tests. --- packages/kbn-journeys/journey/journey_config.ts | 9 +++++++++ .../kbn-journeys/journey/journey_ftr_config.ts | 16 ++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/kbn-journeys/journey/journey_config.ts b/packages/kbn-journeys/journey/journey_config.ts index 53b321aa0c05d..f67abb338c5fb 100644 --- a/packages/kbn-journeys/journey/journey_config.ts +++ b/packages/kbn-journeys/journey/journey_config.ts @@ -70,6 +70,11 @@ export interface ScalabilitySetup { } export interface JourneyConfigOptions { + /** + * Relative path to FTR config file. Use to override the default ones: + * 'x-pack/test/functional/config.base.js', 'test/functional/config.base.js' + */ + ftrConfigPath?: string; /** * Set to `true` to skip this journey. should probably be preceded * by a link to a Github issue where the reasoning for why this was @@ -122,6 +127,10 @@ export class JourneyConfig { this.#opts = opts; } + getFtrConfigPath() { + return this.#opts.ftrConfigPath; + } + getEsArchives() { return this.#opts.esArchives ?? []; } diff --git a/packages/kbn-journeys/journey/journey_ftr_config.ts b/packages/kbn-journeys/journey/journey_ftr_config.ts index 77ac34073558a..3933d0d7e195a 100644 --- a/packages/kbn-journeys/journey/journey_ftr_config.ts +++ b/packages/kbn-journeys/journey/journey_ftr_config.ts @@ -25,16 +25,12 @@ export function makeFtrConfigProvider( steps: AnyStep[] ): FtrConfigProvider { return async ({ readConfigFile }: FtrConfigProviderContext) => { - const baseConfig = ( - await readConfigFile( - Path.resolve( - REPO_ROOT, - config.isXpack() - ? 'x-pack/test/functional/config.base.js' - : 'test/functional/config.base.js' - ) - ) - ).getAll(); + const configPath = config.getFtrConfigPath(); + const defaultConfigPath = config.isXpack() + ? 'x-pack/test/functional/config.base.js' + : 'test/functional/config.base.js'; + const ftrConfigPath = configPath ?? defaultConfigPath; + const baseConfig = (await readConfigFile(Path.resolve(REPO_ROOT, ftrConfigPath))).getAll(); const testBuildId = process.env.BUILDKITE_BUILD_ID ?? `local-${uuidV4()}`; const testJobId = process.env.BUILDKITE_JOB_ID ?? `local-${uuidV4()}`; From ac7ac3f33e61d51dfcb2e19b27e69c74041192a0 Mon Sep 17 00:00:00 2001 From: Nav <13634519+navarone-feekery@users.noreply.github.com> Date: Thu, 16 Feb 2023 09:10:45 +0100 Subject: [PATCH 59/68] [Enterprise Search] Fix full HTML toggle label (#151149) --- .../crawler/crawler_configuration/crawler_configuration.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_configuration/crawler_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_configuration/crawler_configuration.tsx index 660ab114561ad..129d56dd1b72c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_configuration/crawler_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/crawler/crawler_configuration/crawler_configuration.tsx @@ -79,7 +79,7 @@ export const CrawlerConfiguration: React.FC = () => { label={i18n.translate( 'xpack.enterpriseSearch.content.crawler.crawlerConfiguration.extractHTML.extractionSwitchLabel', { - defaultMessage: 'Content extraction.', + defaultMessage: 'Store full HTML', } )} disabled={status === Status.LOADING} From f399dd094e24fd5e7d839aedd2105f9b8b7475ac Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Thu, 16 Feb 2023 09:23:06 +0100 Subject: [PATCH 60/68] Remove unused react-syntax-highlighter resolution (#151295) This resolution was no longer needed as all (sub)dependencies that required this had since been upgraded or removed. --- package.json | 2 -- yarn.lock | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 24aba91e5f45a..4e48afef13c22 100644 --- a/package.json +++ b/package.json @@ -84,8 +84,6 @@ "**/istanbul-lib-coverage": "^3.2.0", "**/minimatch": "^3.1.2", "**/pdfkit/crypto-js": "4.0.0", - "**/react-syntax-highlighter": "^15.3.1", - "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", "**/refractor/prismjs": "~1.27.0", "**/trim": "1.0.1", "**/typescript": "4.6.3", diff --git a/yarn.lock b/yarn.lock index 18f9a96fe74c6..06d1eb93ebdbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17020,7 +17020,7 @@ heap@^0.2.6: resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= -highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@~10.4.0: +highlight.js@^10.1.1, highlight.js@~10.4.0: version "10.4.1" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== From a6073e8458f944f241dfe15ddc8ffda9dd544875 Mon Sep 17 00:00:00 2001 From: Julia Rechkunova Date: Thu, 16 Feb 2023 09:50:02 +0100 Subject: [PATCH 61/68] [Data Views] Show loading indicators when submitting a data view form to prevent multiple requests (#150576) Closes https://github.com/elastic/kibana/issues/146125 This PR adds loading indicators to the form buttons and also disables them when submitting the form. * Creating an ad-hoc data view ![Feb-08-2023 16-00-20](https://user-images.githubusercontent.com/1415710/217567470-53e0c614-d12d-4849-90e1-f157c6c36e79.gif) * Editing an ad-hoc data view ![Feb-08-2023 16-01-38](https://user-images.githubusercontent.com/1415710/217567599-0066393c-4bae-4364-a568-b54c6509d8ad.gif) * Creating a persisted data view ![Feb-08-2023 16-00-57](https://user-images.githubusercontent.com/1415710/217567411-67dd1b3c-99b9-455b-adbe-be21d61344ee.gif) * Editing a persisted data view ![Feb-08-2023 16-02-08](https://user-images.githubusercontent.com/1415710/217567541-a6314611-544f-45f2-944d-f49c7a654a43.gif) --- .../edit_data_view_changed_modal.tsx | 58 +++++++++++-------- .../data_view_editor_flyout_content.tsx | 12 +++- .../public/components/footer/footer.tsx | 15 ++++- .../public/components/footer/index.ts | 2 +- .../public/components/index.ts | 2 +- .../_index_pattern_create_delete.ts | 24 ++++++++ 6 files changed, 83 insertions(+), 30 deletions(-) diff --git a/src/plugins/data_view_editor/public/components/confirm_modals/edit_data_view_changed_modal.tsx b/src/plugins/data_view_editor/public/components/confirm_modals/edit_data_view_changed_modal.tsx index 69ca69fa7c23f..5f0e51a037cfb 100644 --- a/src/plugins/data_view_editor/public/components/confirm_modals/edit_data_view_changed_modal.tsx +++ b/src/plugins/data_view_editor/public/components/confirm_modals/edit_data_view_changed_modal.tsx @@ -15,31 +15,39 @@ interface EditDataViewDeps { onEdit: () => void; } -export const editDataViewModal = ({ dataViewName, overlays, onEdit }: EditDataViewDeps) => - overlays && +export const editDataViewModal = ({ + dataViewName, + overlays, + onEdit, +}: EditDataViewDeps): Promise => overlays - .openConfirm( - i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.modalDescription', { - defaultMessage: 'Changing this data view can break other objects that depend on it.', - }), - { - confirmButtonText: i18n.translate( - 'indexPatternEditor.editDataView.editConfirmationModal.confirmButton', + ? overlays + .openConfirm( + i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.modalDescription', { + defaultMessage: 'Changing this data view can break other objects that depend on it.', + }), { - defaultMessage: 'Confirm', + confirmButtonText: i18n.translate( + 'indexPatternEditor.editDataView.editConfirmationModal.confirmButton', + { + defaultMessage: 'Confirm', + } + ), + title: i18n.translate( + 'indexPatternEditor.editDataView.editConfirmationModal.editHeader', + { + defaultMessage: `Edit '{name}'`, + values: { + name: dataViewName, + }, + } + ), + buttonColor: 'danger', } - ), - title: i18n.translate('indexPatternEditor.editDataView.editConfirmationModal.editHeader', { - defaultMessage: `Edit '{name}'`, - values: { - name: dataViewName, - }, - }), - buttonColor: 'danger', - } - ) - .then(async (isConfirmed) => { - if (isConfirmed) { - onEdit(); - } - }); + ) + .then(async (isConfirmed) => { + if (isConfirmed) { + await onEdit(); + } + }) + : Promise.resolve(); diff --git a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx index d78f54995df1d..e6987c11a920b 100644 --- a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx @@ -48,6 +48,7 @@ import { NameField, schema, Footer, + SubmittingType, AdvancedParamsContent, PreviewPanel, RollupBetaWarning, @@ -136,7 +137,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ } if (editData && editData.getIndexPattern() !== formData.title) { - editDataViewModal({ + await editDataViewModal({ dataViewName: formData.name || formData.title, overlays, onEdit: async () => { @@ -301,7 +302,14 @@ const IndexPatternEditorFlyoutContentComponent = ({ form.setFieldValue('isAdHoc', adhoc || false); form.submit(); }} - submitDisabled={form.isSubmitted && !form.isValid} + submitDisabled={(form.isSubmitted && !form.isValid) || form.isSubmitting} + submittingType={ + form.isSubmitting + ? form.getFormData().isAdHoc + ? SubmittingType.savingAsAdHoc + : SubmittingType.persisting + : undefined + } isEdit={!!editData} isPersisted={Boolean(editData && editData.isPersisted())} allowAdHoc={allowAdHoc} diff --git a/src/plugins/data_view_editor/public/components/footer/footer.tsx b/src/plugins/data_view_editor/public/components/footer/footer.tsx index 024885e91d548..af7dd376ecb34 100644 --- a/src/plugins/data_view_editor/public/components/footer/footer.tsx +++ b/src/plugins/data_view_editor/public/components/footer/footer.tsx @@ -17,9 +17,15 @@ import { EuiButton, } from '@elastic/eui'; +export enum SubmittingType { + savingAsAdHoc = 'savingAsAdHoc', + persisting = 'persisting', +} + interface FooterProps { onCancel: () => void; onSubmit: (isAdHoc?: boolean) => void; + submittingType: SubmittingType | undefined; submitDisabled: boolean; isEdit: boolean; isPersisted: boolean; @@ -53,12 +59,14 @@ const exploreButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutExplo export const Footer = ({ onCancel, onSubmit, + submittingType, submitDisabled, isEdit, allowAdHoc, isPersisted, canSave, }: FooterProps) => { + const isEditingAdHoc = isEdit && !isPersisted; const submitPersisted = () => { onSubmit(false); }; @@ -89,6 +97,7 @@ export const Footer = ({ onClick={submitAdHoc} data-test-subj="exploreIndexPatternButton" disabled={submitDisabled} + isLoading={submittingType === SubmittingType.savingAsAdHoc} title={i18n.translate('indexPatternEditor.editor.flyoutExploreButtonTitle', { defaultMessage: 'Use this data view without creating a saved object', })} @@ -98,7 +107,7 @@ export const Footer = ({ )} - {(canSave || (isEdit && !isPersisted)) && ( + {(canSave || isEditingAdHoc) && ( {isEdit ? isPersisted diff --git a/src/plugins/data_view_editor/public/components/footer/index.ts b/src/plugins/data_view_editor/public/components/footer/index.ts index e3103aab170e0..6649c1ac6f064 100644 --- a/src/plugins/data_view_editor/public/components/footer/index.ts +++ b/src/plugins/data_view_editor/public/components/footer/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { Footer } from './footer'; +export { Footer, SubmittingType } from './footer'; diff --git a/src/plugins/data_view_editor/public/components/index.ts b/src/plugins/data_view_editor/public/components/index.ts index d47f964e3c602..20bb30a3ed11f 100644 --- a/src/plugins/data_view_editor/public/components/index.ts +++ b/src/plugins/data_view_editor/public/components/index.ts @@ -15,6 +15,6 @@ export { schema } from './form_schema'; export { NameField, TimestampField, TypeField, TitleField } from './form_fields'; export { PreviewPanel } from './preview_panel'; export { LoadingIndices } from './loading_indices'; -export { Footer } from './footer'; +export { Footer, SubmittingType } from './footer'; export { AdvancedParamsContent } from './advanced_params_content'; export { RollupBetaWarning } from './rollup_beta_warning'; diff --git a/test/functional/apps/management/_index_pattern_create_delete.ts b/test/functional/apps/management/_index_pattern_create_delete.ts index f89013eac43fa..7a44cc2f63592 100644 --- a/test/functional/apps/management/_index_pattern_create_delete.ts +++ b/test/functional/apps/management/_index_pattern_create_delete.ts @@ -186,6 +186,30 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await testSubjects.exists('field-name-agent')).to.be(true); }); }); + + it('should disable Save button after pressing', async function () { + await PageObjects.settings.clickEditIndexButton(); + await PageObjects.header.waitUntilLoadingHasFinished(); + + await retry.try(async () => { + await PageObjects.settings.setIndexPatternField('logs*'); + }); + await PageObjects.settings.selectTimeFieldOption('@timestamp'); + + expect(await testSubjects.isEnabled('saveIndexPatternButton')).to.be(true); + await (await PageObjects.settings.getSaveDataViewButtonActive()).click(); + + // wait for the confirmation modal to open + await retry.waitFor('confirmation modal', async () => { + return await testSubjects.exists('confirmModalConfirmButton'); + }); + + // while the confirmation modal is open, we can check that the form button has actually become disabled + expect(await testSubjects.isEnabled('saveIndexPatternButton')).to.be(false); + + await testSubjects.click('confirmModalConfirmButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); }); describe('index pattern deletion', function indexDelete() { From 17beb0e6476627368f4b8f9eed343917f543c2d4 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Thu, 16 Feb 2023 10:11:16 +0100 Subject: [PATCH 62/68] [ML] Transforms: Health status functional tests, expanded row tab. (#150664) Adds functional tests for transform health status and expanded row tab. --- .../common/api_schemas/transforms.ts | 2 + x-pack/plugins/transform/common/constants.ts | 5 +- .../transform/common/types/transform_stats.ts | 14 ++- .../transform_list/expanded_row.tsx | 25 ++++- .../expanded_row_health_pane.tsx | 98 ++++++++++++++++ .../expanded_row_messages_pane.tsx | 25 +++-- .../transform_health_colored_dot.tsx | 14 ++- .../index_pattern/creation_index_pattern.ts | 5 + .../creation_saved_search.ts | 3 + .../apps/transform/edit_clone/editing.ts | 3 + .../test/functional/apps/transform/helpers.ts | 8 +- .../transform/start_reset_delete/deleting.ts | 4 + .../transform/start_reset_delete/resetting.ts | 4 + .../transform/start_reset_delete/starting.ts | 106 +++++++++++++++++- .../test/functional/services/transform/api.ts | 8 +- .../services/transform/transform_table.ts | 48 ++++++++ 16 files changed, 341 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_health_pane.tsx diff --git a/x-pack/plugins/transform/common/api_schemas/transforms.ts b/x-pack/plugins/transform/common/api_schemas/transforms.ts index 28634e280ee34..5b300a613be94 100644 --- a/x-pack/plugins/transform/common/api_schemas/transforms.ts +++ b/x-pack/plugins/transform/common/api_schemas/transforms.ts @@ -72,6 +72,8 @@ export const settingsSchema = schema.object({ docs_per_second: schema.maybe(schema.nullable(schema.number())), // Optional value that takes precedence over cluster's setting. num_failure_retries: schema.maybe(schema.nullable(schema.number())), + // Unattended mode flag + unattended: schema.maybe(schema.boolean()), }); export const sourceSchema = schema.object({ diff --git a/x-pack/plugins/transform/common/constants.ts b/x-pack/plugins/transform/common/constants.ts index dceda770827c7..20f4d4ff77a6f 100644 --- a/x-pack/plugins/transform/common/constants.ts +++ b/x-pack/plugins/transform/common/constants.ts @@ -130,7 +130,7 @@ export const TRANSFORM_HEALTH_LABEL = { export const TRANSFORM_HEALTH_DESCRIPTION = { green: i18n.translate('xpack.transform.transformHealth.greenDescription', { - defaultMessage: 'The transform is healthy.', + defaultMessage: 'The transform is running as expected.', }), unknown: i18n.translate('xpack.transform.transformHealth.unknownDescription', { defaultMessage: 'The health of the transform could not be determined.', @@ -197,3 +197,6 @@ export const DEFAULT_CONTINUOUS_MODE_DELAY = '60s'; export const DEFAULT_TRANSFORM_FREQUENCY = '1m'; export const DEFAULT_TRANSFORM_SETTINGS_DOCS_PER_SECOND = null; export const DEFAULT_TRANSFORM_SETTINGS_MAX_PAGE_SEARCH_SIZE = 500; + +// Used in the transform list's expanded row for the messages and issues table. +export const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; diff --git a/x-pack/plugins/transform/common/types/transform_stats.ts b/x-pack/plugins/transform/common/types/transform_stats.ts index 17e5f5a3fa936..c1194ea8f1018 100644 --- a/x-pack/plugins/transform/common/types/transform_stats.ts +++ b/x-pack/plugins/transform/common/types/transform_stats.ts @@ -10,6 +10,13 @@ import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { type TransformHealth, type TransformState, TRANSFORM_STATE } from '../constants'; import { TransformId } from './transform'; +export interface TransformHealthIssue { + issue: string; + details?: string; + count: number; + first_occurrence?: number; +} + export interface TransformStats { id: TransformId; checkpointing: { @@ -29,12 +36,7 @@ export interface TransformStats { }; health: { status: TransformHealth; - issues?: Array<{ - issue: string; - details?: string; - count: number; - first_occurrence?: number; - }>; + issues?: TransformHealthIssue[]; }; node?: { id: string; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx index 314162e9b3ea1..43e20106ee3ab 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx @@ -6,6 +6,7 @@ */ import React, { FC, useMemo } from 'react'; +import { css } from '@emotion/react'; import moment from 'moment-timezone'; import { EuiButtonEmpty, EuiTabbedContent } from '@elastic/eui'; @@ -25,6 +26,7 @@ import { ExpandedRowDetailsPane, SectionConfig, SectionItem } from './expanded_r import { ExpandedRowJsonPane } from './expanded_row_json_pane'; import { ExpandedRowMessagesPane } from './expanded_row_messages_pane'; import { ExpandedRowPreviewPane } from './expanded_row_preview_pane'; +import { ExpandedRowHealthPane } from './expanded_row_health_pane'; import { TransformHealthColoredDot } from './transform_health_colored_dot'; function getItemDescription(value: any) { @@ -258,6 +260,21 @@ export const ExpandedRow: FC = ({ item, onAlertEdit }) => { name: 'JSON', content: , }, + ...(item.stats.health + ? [ + { + id: `transform-health-tab-${tabId}`, + 'data-test-subj': 'transformHealthTab', + name: i18n.translate( + 'xpack.transform.transformList.transformDetails.tabs.transformHealthLabel', + { + defaultMessage: 'Health', + } + ), + content: , + }, + ] + : []), { id: `transform-messages-tab-${tabId}`, 'data-test-subj': 'transformMessagesTab', @@ -293,7 +310,13 @@ export const ExpandedRow: FC = ({ item, onAlertEdit }) => { initialSelectedTab={tabs[0]} onTabClick={() => {}} expand={false} - style={{ width: '100%' }} + css={css` + width: 100%; + + .euiTable { + background-color: transparent; + } + `} data-test-subj="transformExpandedRowTabbedContent" /> ); diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_health_pane.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_health_pane.tsx new file mode 100644 index 0000000000000..0e8ab94dc2086 --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_health_pane.tsx @@ -0,0 +1,98 @@ +/* + * 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, { type FC } from 'react'; + +import { formatDate, EuiPanel, EuiSpacer, EuiInMemoryTable } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { TIME_FORMAT } from '../../../../../../common/constants'; +import type { TransformHealthIssue } from '../../../../../../common/types/transform_stats'; + +import { TransformListRow } from '../../../../common'; + +import { TransformHealthColoredDot } from './transform_health_colored_dot'; + +interface ExpandedRowHealthPaneProps { + health: TransformListRow['stats']['health']; +} + +export const ExpandedRowHealthPane: FC = ({ health }) => { + const { status, issues } = health; + + const sorting = { + sort: { + field: 'first_occurrence' as const, + direction: 'desc' as const, + }, + }; + + const columns = [ + { + field: 'first_occurrence', + name: i18n.translate( + 'xpack.transform.transformList.transformDetails.healthPane.firstOccurrenceLabel', + { + defaultMessage: 'First occurrence', + } + ), + render: (firstOccurrence: number) => formatDate(firstOccurrence, TIME_FORMAT), + sortable: true, + }, + { + field: 'count', + name: i18n.translate('xpack.transform.transformList.transformDetails.healthPane.countLabel', { + defaultMessage: 'count', + }), + sortable: true, + width: '60px', + }, + { + field: 'issue', + name: i18n.translate('xpack.transform.transformList.transformDetails.healthPane.issueLabel', { + defaultMessage: 'Issue', + }), + sortable: true, + }, + { + field: 'details', + name: i18n.translate( + 'xpack.transform.transformList.transformDetails.healthPane.detailsLabel', + { + defaultMessage: 'Details', + } + ), + width: '50%', + }, + ]; + + return ( + + + + {Array.isArray(issues) && issues.length > 0 && ( + <> + + + data-test-subj="transformHealthTabContentIssueTable" + items={issues} + columns={columns} + compressed={true} + pagination={issues.length > 10} + sorting={sorting} + /> + + )} + + ); +}; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx index 2b428ddf10505..4cf387ad2973b 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx @@ -5,22 +5,21 @@ * 2.0. */ -import React, { MouseEvent, useState } from 'react'; +import React, { MouseEvent, useState, type FC } from 'react'; import { - EuiSpacer, + formatDate, + EuiPanel, EuiBasicTable, EuiBasicTableProps, EuiToolTip, EuiButtonIcon, } from '@elastic/eui'; -// @ts-ignore -import { formatDate } from '@elastic/eui/lib/services/format'; import { euiLightVars as theme } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; -import { DEFAULT_MAX_AUDIT_MESSAGE_SIZE } from '../../../../../../common/constants'; +import { DEFAULT_MAX_AUDIT_MESSAGE_SIZE, TIME_FORMAT } from '../../../../../../common/constants'; import { isGetTransformsAuditMessagesResponseSchema } from '../../../../../../common/api_schemas/type_guards'; import { TransformMessage } from '../../../../../../common/types/messages'; @@ -28,9 +27,7 @@ import { useApi } from '../../../../hooks/use_api'; import { JobIcon } from '../../../../components/job_icon'; import { useRefreshTransformList } from '../../../../common'; -const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; - -interface Props { +interface ExpandedRowMessagesPaneProps { transformId: string; } @@ -39,7 +36,7 @@ interface Sorting { direction: 'asc' | 'desc'; } -export const ExpandedRowMessagesPane: React.FC = ({ transformId }) => { +export const ExpandedRowMessagesPane: FC = ({ transformId }) => { const [messages, setMessages] = useState([]); const [msgCount, setMsgCount] = useState(0); @@ -218,8 +215,12 @@ export const ExpandedRowMessagesPane: React.FC = ({ transformId }) => { }; return ( -
- + = ({ transformId }) => { onChange={onChange} sorting={sorting} /> -
+ ); }; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_health_colored_dot.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_health_colored_dot.tsx index 2bb3a9c22654d..85fbc2c604fc1 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_health_colored_dot.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_health_colored_dot.tsx @@ -18,14 +18,22 @@ import { interface TransformHealthProps { healthStatus: TransformHealth; + compact?: boolean; } -export const TransformHealthColoredDot: FC = ({ healthStatus }) => { - return ( +export const TransformHealthColoredDot: FC = ({ + healthStatus, + compact = true, +}) => { + return compact ? ( - {TRANSFORM_HEALTH_LABEL[healthStatus]} + {TRANSFORM_HEALTH_LABEL[healthStatus]} + ) : ( + + {TRANSFORM_HEALTH_LABEL[healthStatus]} {TRANSFORM_HEALTH_DESCRIPTION[healthStatus]} + ); }; diff --git a/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts index e8ded6f662867..017d2466fb90d 100644 --- a/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts +++ b/x-pack/test/functional/apps/transform/creation/index_pattern/creation_index_pattern.ts @@ -185,6 +185,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', + health: 'Healthy', }, indexPreview: { columns: 10, @@ -368,6 +369,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', + health: 'Healthy', }, indexPreview: { columns: 10, @@ -432,6 +434,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', + health: 'Healthy', }, indexPreview: { columns: 10, @@ -479,6 +482,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', + health: 'Healthy', }, indexPreview: { columns: 10, @@ -819,6 +823,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { status: testData.expected.row.status, mode: testData.expected.row.mode, progress: testData.expected.row.progress, + health: testData.expected.row.health, }); }); diff --git a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts index 9f985a16da98d..c976be55a7885 100644 --- a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts +++ b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_saved_search.ts @@ -68,6 +68,7 @@ export default function ({ getService }: FtrProviderContext) { status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', + health: 'Healthy', }, sourceIndex: 'ft_farequote', indexPreview: { @@ -105,6 +106,7 @@ export default function ({ getService }: FtrProviderContext) { status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', + health: 'Healthy', }, sourceIndex: 'ft_farequote', indexPreview: { @@ -297,6 +299,7 @@ export default function ({ getService }: FtrProviderContext) { status: testData.expected.row.status, mode: testData.expected.row.mode, progress: testData.expected.row.progress, + health: testData.expected.row.health, }); await transform.testExecution.logTestStep( diff --git a/x-pack/test/functional/apps/transform/edit_clone/editing.ts b/x-pack/test/functional/apps/transform/edit_clone/editing.ts index 5f767825c7c31..10cacc361779c 100644 --- a/x-pack/test/functional/apps/transform/edit_clone/editing.ts +++ b/x-pack/test/functional/apps/transform/edit_clone/editing.ts @@ -73,6 +73,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'pivot', mode: 'batch', progress: '100', + health: 'Healthy', }, }, }, @@ -95,6 +96,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'latest', mode: 'batch', progress: '100', + health: 'Healthy', }, }, }, @@ -231,6 +233,7 @@ export default function ({ getService }: FtrProviderContext) { status: testData.expected.row.status, mode: testData.expected.row.mode, progress: testData.expected.row.progress, + health: testData.expected.row.health, }); await transform.testExecution.logTestStep( diff --git a/x-pack/test/functional/apps/transform/helpers.ts b/x-pack/test/functional/apps/transform/helpers.ts index d4b163871cc4c..831cefce6afad 100644 --- a/x-pack/test/functional/apps/transform/helpers.ts +++ b/x-pack/test/functional/apps/transform/helpers.ts @@ -60,11 +60,14 @@ export function isLatestTransformTestData(arg: any): arg is LatestTransformTestD export function getPivotTransformConfig( prefix: string, - continuous?: boolean + continuous?: boolean, + unattended?: boolean ): TransformPivotConfig { const timestamp = Date.now(); return { - id: `ec_${prefix}_pivot_${timestamp}_${continuous ? 'cont' : 'batch'}`, + id: `ec_${prefix}_pivot_${timestamp}_${continuous ? 'cont' : 'batch'}${ + unattended ? '_unattended' : '' + }`, source: { index: ['ft_ecommerce'] }, pivot: { group_by: { category: { terms: { field: 'category.keyword' } } }, @@ -75,6 +78,7 @@ export function getPivotTransformConfig( } transform with avg(products.base_price) grouped by terms(category.keyword)`, dest: { index: `user-ec_2_${timestamp}` }, ...(continuous ? { sync: { time: { field: 'order_date', delay: '60s' } } } : {}), + ...(unattended ? { settings: { unattended: true } } : {}), }; } diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/deleting.ts b/x-pack/test/functional/apps/transform/start_reset_delete/deleting.ts index e76ef118fde0d..80a00e0b09dfd 100644 --- a/x-pack/test/functional/apps/transform/start_reset_delete/deleting.ts +++ b/x-pack/test/functional/apps/transform/start_reset_delete/deleting.ts @@ -27,6 +27,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'pivot', mode: 'batch', progress: 100, + health: 'Healthy', }, }, }, @@ -39,6 +40,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'pivot', mode: 'continuous', progress: undefined, + health: 'Healthy', }, }, }, @@ -55,6 +57,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'latest', mode: 'batch', progress: 100, + health: 'Healthy', }, }, }, @@ -114,6 +117,7 @@ export default function ({ getService }: FtrProviderContext) { status: testData.expected.row.status, mode: testData.expected.row.mode, progress: testData.expected.row.progress, + health: testData.expected.row.health, }); await transform.testExecution.logTestStep('should show the delete modal'); diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/resetting.ts b/x-pack/test/functional/apps/transform/start_reset_delete/resetting.ts index ef62d9986813d..c6e5180dfa8fb 100644 --- a/x-pack/test/functional/apps/transform/start_reset_delete/resetting.ts +++ b/x-pack/test/functional/apps/transform/start_reset_delete/resetting.ts @@ -28,6 +28,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'pivot', mode: 'batch', progress: 100, + health: 'Healthy', }, }, }, @@ -41,6 +42,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'pivot', mode: 'continuous', progress: undefined, + health: 'Healthy', }, }, }, @@ -57,6 +59,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'latest', mode: 'batch', progress: 100, + health: 'Healthy', }, }, }, @@ -116,6 +119,7 @@ export default function ({ getService }: FtrProviderContext) { status: testData.expected.row.status, mode: testData.expected.row.mode, progress: testData.expected.row.progress, + health: testData.expected.row.health, }); await transform.testExecution.logTestStep('should show the reset modal'); diff --git a/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts b/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts index 1fccec9a9192b..3a06dbb6108c7 100644 --- a/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts +++ b/x-pack/test/functional/apps/transform/start_reset_delete/starting.ts @@ -5,36 +5,106 @@ * 2.0. */ -import { TRANSFORM_STATE } from '@kbn/transform-plugin/common/constants'; +import { + TRANSFORM_STATE, + TRANSFORM_HEALTH, + TRANSFORM_HEALTH_LABEL, + TRANSFORM_HEALTH_DESCRIPTION, +} from '@kbn/transform-plugin/common/constants'; +import { + TransformLatestConfig, + TransformPivotConfig, +} from '@kbn/transform-plugin/common/types/transform'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { getLatestTransformConfig, getPivotTransformConfig } from '../helpers'; +interface TestDataPivot { + suiteTitle: string; + originalConfig: TransformPivotConfig; + mode: 'batch' | 'continuous'; + type: 'pivot'; + expected: { + healthDescription: string; + healthLabel: string; + healthStatus: string; + }; +} + +interface TestDataLatest { + suiteTitle: string; + originalConfig: TransformLatestConfig; + mode: 'batch' | 'continuous'; + type: 'latest'; + expected: { + healthDescription: string; + healthLabel: string; + healthStatus: string; + }; +} + +type TestData = TestDataPivot | TestDataLatest; + export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const transform = getService('transform'); describe('starting', function () { const PREFIX = 'starting'; - const testDataList = [ + const testDataList: TestData[] = [ { suiteTitle: 'batch transform with pivot configuration', originalConfig: getPivotTransformConfig(PREFIX, false), mode: 'batch', + type: 'pivot', + expected: { + healthDescription: TRANSFORM_HEALTH_DESCRIPTION.green, + healthLabel: TRANSFORM_HEALTH_LABEL.green, + healthStatus: TRANSFORM_HEALTH.GREEN, + }, }, { suiteTitle: 'continuous transform with pivot configuration', originalConfig: getPivotTransformConfig(PREFIX, true), mode: 'continuous', + type: 'pivot', + expected: { + healthDescription: TRANSFORM_HEALTH_DESCRIPTION.green, + healthLabel: TRANSFORM_HEALTH_LABEL.green, + healthStatus: TRANSFORM_HEALTH.GREEN, + }, + }, + { + suiteTitle: 'non healthy continuous transform with pivot configuration', + originalConfig: getPivotTransformConfig(PREFIX, true, true), + mode: 'continuous', + type: 'pivot', + expected: { + healthDescription: TRANSFORM_HEALTH_DESCRIPTION.yellow, + healthLabel: TRANSFORM_HEALTH_LABEL.yellow, + healthStatus: TRANSFORM_HEALTH.YELLOW, + }, }, { suiteTitle: 'batch transform with latest configuration', originalConfig: getLatestTransformConfig(PREFIX, false), mode: 'batch', + type: 'latest', + expected: { + healthDescription: TRANSFORM_HEALTH_DESCRIPTION.green, + healthLabel: TRANSFORM_HEALTH_LABEL.green, + healthStatus: TRANSFORM_HEALTH.GREEN, + }, }, { suiteTitle: 'continuous transform with latest configuration', originalConfig: getLatestTransformConfig(PREFIX, true), mode: 'continuous', + type: 'latest', + expected: { + healthDescription: TRANSFORM_HEALTH_DESCRIPTION.green, + healthLabel: TRANSFORM_HEALTH_LABEL.green, + healthStatus: TRANSFORM_HEALTH.GREEN, + }, }, ]; @@ -43,7 +113,23 @@ export default function ({ getService }: FtrProviderContext) { await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); for (const testData of testDataList) { - await transform.api.createTransform(testData.originalConfig.id, testData.originalConfig); + if ( + testData.expected.healthStatus === TRANSFORM_HEALTH.YELLOW && + testData.type === 'pivot' + ) { + testData.originalConfig.pivot.aggregations['products.base_price.fail'] = { + avg: { + script: { + source: "def a = doc['non_existing'].value", + }, + }, + }; + } + await transform.api.createTransform( + testData.originalConfig.id, + testData.originalConfig, + testData.expected.healthStatus === TRANSFORM_HEALTH.YELLOW + ); } await transform.testResources.setKibanaTimeZoneToUTC(); await transform.securityUI.loginAsTransformPowerUser(); @@ -80,6 +166,12 @@ export default function ({ getService }: FtrProviderContext) { await transform.table.assertTransformRowActionEnabled(transformId, 'Start', true); await transform.table.clickTransformRowAction(transformId, 'Start'); await transform.table.confirmStartTransform(); + + await transform.table.assertTransformExpandedRowHealth( + testData.expected.healthDescription, + testData.expected.healthStatus !== TRANSFORM_HEALTH.GREEN + ); + await transform.table.clearSearchString(testDataList.length); if (testData.mode === 'continuous') { @@ -88,10 +180,16 @@ export default function ({ getService }: FtrProviderContext) { testData.originalConfig.id, TRANSFORM_STATE.STOPPED ); - } else { + } else if (testData.mode === 'batch') { + await transform.testExecution.logTestStep('should display a healthy status'); await transform.table.assertTransformRowProgressGreaterThan(transformId, 0); } + await transform.table.assertTransformRowHealth( + testData.originalConfig.id, + testData.expected.healthLabel + ); + await transform.table.assertTransformRowStatusNotEql( testData.originalConfig.id, TRANSFORM_STATE.FAILED diff --git a/x-pack/test/functional/services/transform/api.ts b/x-pack/test/functional/services/transform/api.ts index 30a3da31a5dbe..dcb76bb23eaf0 100644 --- a/x-pack/test/functional/services/transform/api.ts +++ b/x-pack/test/functional/services/transform/api.ts @@ -215,10 +215,14 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { return body as TransformPivotConfig; }, - async createTransform(transformId: string, transformConfig: PutTransformsRequestSchema) { + async createTransform( + transformId: string, + transformConfig: PutTransformsRequestSchema, + deferValidation?: boolean + ) { log.debug(`Creating transform with id '${transformId}'...`); const { body, status } = await esSupertest - .put(`/_transform/${transformId}`) + .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) .send(transformConfig); this.assertResponseStatusCode(200, status, body); diff --git a/x-pack/test/functional/services/transform/transform_table.ts b/x-pack/test/functional/services/transform/transform_table.ts index 785d7e33d5042..4ae7e4c95821d 100644 --- a/x-pack/test/functional/services/transform/transform_table.ts +++ b/x-pack/test/functional/services/transform/transform_table.ts @@ -57,6 +57,11 @@ export function TransformTableProvider({ getService }: FtrProviderContext) { .findTestSubject('transformListColumnProgress') .findTestSubject('transformListProgress') .attr('value'), + health: $tr + .findTestSubject('transformListColumnHealth') + .findTestSubject('transformListHealth') + .text() + .trim(), }); } @@ -140,6 +145,18 @@ export function TransformTableProvider({ getService }: FtrProviderContext) { }); } + public async assertTransformRowHealth(transformId: string, health: string) { + await retry.tryForTime(30 * 1000, async () => { + await this.refreshTransformList(); + const rows = await this.parseTransformTable(); + const transformRow = rows.filter((row) => row.id === transformId)[0]; + expect(transformRow.health).to.eql( + health, + `Expected transform row status to not be '${health}' (got '${transformRow.health}')` + ); + }); + } + public async assertTransformRowStatusNotEql(transformId: string, status: string) { await retry.tryForTime(30 * 1000, async () => { await this.refreshTransformList(); @@ -187,6 +204,7 @@ export function TransformTableProvider({ getService }: FtrProviderContext) { // Walk through the rest of the tabs and check if the corresponding content shows up await this.switchToExpandedRowTab('transformJsonTab', '~transformJsonTabContent'); + await this.switchToExpandedRowTab('transformHealthTab', '~transformHealthTabContent'); await this.switchToExpandedRowTab('transformMessagesTab', '~transformMessagesTabContent'); await this.switchToExpandedRowTab('transformPreviewTab', '~transformPivotPreview'); } @@ -240,6 +258,36 @@ export function TransformTableProvider({ getService }: FtrProviderContext) { await this.switchToExpandedRowTab('transformDetailsTab', '~transformDetailsTabContent'); } + public async assertTransformExpandedRowHealth( + expectedText: string, + expectIssueTableToExist: boolean + ) { + await this.ensureDetailsOpen(); + + // The expanded row should show the details tab content by default + await testSubjects.existOrFail('transformDetailsTab'); + await testSubjects.existOrFail('~transformDetailsTabContent'); + + // Click on the messages tab and assert the messages + await this.switchToExpandedRowTab('transformHealthTab', '~transformHealthTabContent'); + await retry.tryForTime(30 * 1000, async () => { + const actualText = await testSubjects.getVisibleText('~transformHealthTabContent'); + expect(actualText.toLowerCase()).to.contain( + expectedText.toLowerCase(), + `Expected transform messages text to include '${expectedText}'` + ); + }); + + if (expectIssueTableToExist) { + await testSubjects.existOrFail('transformHealthTabContentIssueTable'); + } else { + await testSubjects.missingOrFail('transformHealthTabContentIssueTable'); + } + + // Switch back to details tab + await this.switchToExpandedRowTab('transformDetailsTab', '~transformDetailsTabContent'); + } + public rowSelector(transformId: string, subSelector?: string) { const row = `~transformListTable > ~row-${transformId}`; return !subSelector ? row : `${row} > ${subSelector}`; From 108b4b512b0d5d5e75f7e603aa5f8bc9fc0f3f13 Mon Sep 17 00:00:00 2001 From: mohamedhamed-ahmed Date: Thu, 16 Feb 2023 09:23:27 +0000 Subject: [PATCH 63/68] fix(bug): fixed sorting on cluster list table (#151372) ## Summary Going to Stack Monitoring in Kibana will bring you to the cluster list. Attempt to click one of the table headings to sort by that field. Expected behavior: The table values are sorted by that field Actual behavior: It changes the direction of the sort on Name closes [#147273](https://github.com/elastic/kibana/issues/147273) ### Testing https://user-images.githubusercontent.com/11225826/219131518-7a9b341a-3b6e-4a46-a680-a1d2a12b2bb1.mov --- .../components/cluster/listing/listing.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js index 77a73e51ea794..612b6e9154689 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js @@ -389,17 +389,21 @@ const StandaloneClusterCallout = ({ changeCluster, storage }) => { }; export const Listing = ({ angular, clusters, sorting, pagination, onTableChange }) => { + const { scope, globalState, storage, showLicenseExpiration } = angular; + const { + sort: { direction, field }, + } = sorting; const { services } = useKibana(); - const _changeCluster = partial(changeCluster, angular.scope, angular.globalState); + const _changeCluster = partial(changeCluster, scope, globalState); const _handleClickIncompatibleLicense = partial( handleClickIncompatibleLicense, - angular.scope, + scope, services.theme.theme$ ); const _handleClickInvalidLicense = partial( handleClickInvalidLicense, - angular.scope, + scope, services.theme.theme$ ); const hasStandaloneCluster = !!clusters.find( @@ -411,13 +415,13 @@ export const Listing = ({ angular, clusters, sorting, pagination, onTableChange {hasStandaloneCluster ? ( - + ) : null} Date: Thu, 16 Feb 2023 11:39:03 +0200 Subject: [PATCH 64/68] [Cloud Security] Improved resource tab (#151335) --- .../__snapshots__/resource_tab.test.ts.snap | 100 --------------- .../findings_flyout/resource_tab.test.ts | 24 ---- .../findings/findings_flyout/resource_tab.tsx | 119 +++++++----------- .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 6 files changed, 48 insertions(+), 201 deletions(-) delete mode 100644 x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap delete mode 100644 x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap deleted file mode 100644 index ffa5d91e2d677..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/__snapshots__/resource_tab.test.ts.snap +++ /dev/null @@ -1,100 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`prepareDescriptionList create description lists accordingly 1`] = ` -Array [ - Object { - "description": - string - , - "title": - - a - - , - }, - Object { - "description": - 123 - , - "title": - - b - - , - }, - Object { - "description": "undefined", - "title": - - c - - , - }, - Object { - "description": - null - , - "title": - - d - - , - }, - Object { - "description": - true - , - "title": - - e - - , - }, - Object { - "description": - false - , - "title": - - f - - , - }, - Object { - "description": - [ - { - "a": "another string", - "b": 123 - } -] - , - "title": - - g - - , - }, -] -`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts deleted file mode 100644 index 94886e8fb254d..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.test.ts +++ /dev/null @@ -1,24 +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 { prepareDescriptionList } from './resource_tab'; - -const mockData = { - a: 'string', - b: 123, - c: undefined, - d: null, - e: true, - f: false, - g: [{ a: 'another string', b: 123 }], -}; - -describe('prepareDescriptionList', () => { - it('create description lists accordingly', () => { - const descriptionList = prepareDescriptionList(mockData); - expect(descriptionList).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx index 86ea3f67acd79..f4e0a5842231f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings_flyout/resource_tab.tsx @@ -6,20 +6,22 @@ */ import { - EuiAccordion, EuiCode, EuiCodeBlock, - EuiDescriptionList, - EuiPanel, - EuiSpacer, + EuiInMemoryTable, + EuiInMemoryTableProps, EuiText, - useEuiTheme, } from '@elastic/eui'; -import React, { useMemo } from 'react'; +import React from 'react'; import { getFlattenedObject } from '@kbn/std'; import { i18n } from '@kbn/i18n'; import { CspFinding } from '../../../../common/schemas/csp_finding'; +interface ResourceItem { + key: string; // flattened dot notation object path for CspFinding['resource']; + value: unknown; +} + const getDescriptionDisplay = (value: unknown) => { if (value === undefined) return 'undefined'; if (typeof value === 'boolean' || value === null) { @@ -37,71 +39,46 @@ const getDescriptionDisplay = (value: unknown) => { return {value as string}; }; -export const prepareDescriptionList = (data: any) => - Object.entries(getFlattenedObject(data)) - .sort((a, b) => a[0].localeCompare(b[0])) - .map(([key, value]) => ({ - title: ( - - {key} - - ), - description: getDescriptionDisplay(value), - })); - -export const ResourceTab = ({ data }: { data: CspFinding }) => { - const { euiTheme } = useEuiTheme(); +const search: EuiInMemoryTableProps['search'] = { + box: { + incremental: true, + }, +}; - const accordions = useMemo( - () => [ - { - title: i18n.translate( - 'xpack.csp.findings.findingsFlyout.resourceTab.resourceAccordionTitle', - { defaultMessage: 'Resource' } - ), - id: 'resourceAccordion', - listItems: prepareDescriptionList(data.resource), - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.resourceTab.hostAccordionTitle', { - defaultMessage: 'Host', - }), - id: 'hostAccordion', - listItems: prepareDescriptionList(data.host), - }, - ], - [data.host, data.resource] - ); +const sorting: EuiInMemoryTableProps['sorting'] = { + sort: { + field: 'key', + direction: 'asc', + }, +}; - return ( - <> - {accordions.map((accordion) => ( - - - - {accordion.title} - - } - arrowDisplay="left" - initialIsOpen - > - - - - - - ))} - - ); +const pagination: EuiInMemoryTableProps['pagination'] = { + initialPageSize: 100, + showPerPageOptions: false, }; + +const columns: EuiInMemoryTableProps['columns'] = [ + { + field: 'key', + name: i18n.translate('xpack.csp.flyout.resource.fieldLabel', { defaultMessage: 'Field' }), + width: '25%', + }, + { + field: 'value', + name: i18n.translate('xpack.csp.flyout.resource.fieldValueLabel', { defaultMessage: 'Value' }), + render: (value, record) =>
{getDescriptionDisplay(value)}
, + }, +]; + +const getFlattenedItems = (resource: CspFinding['resource']) => + Object.entries(getFlattenedObject(resource)).map(([key, value]) => ({ key, value })); + +export const ResourceTab = ({ data }: { data: CspFinding }) => ( + +); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 5a609c618f48c..6844a048033de 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -10090,8 +10090,6 @@ "xpack.csp.findings.findingsFlyout.overviewTab.ruleNameTitle": "Nom de règle", "xpack.csp.findings.findingsFlyout.overviewTab.ruleTagsTitle": "Balises de règle", "xpack.csp.findings.findingsFlyout.overviewTabTitle": "Aperçu", - "xpack.csp.findings.findingsFlyout.resourceTab.hostAccordionTitle": "Hôte", - "xpack.csp.findings.findingsFlyout.resourceTab.resourceAccordionTitle": "Ressource", "xpack.csp.findings.findingsFlyout.resourceTabTitle": "Ressource", "xpack.csp.findings.findingsFlyout.ruleTab.auditTitle": "Audit", "xpack.csp.findings.findingsFlyout.ruleTab.benchmarkTitle": "Benchmark", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 400dba199ca16..da25100e1e288 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10079,8 +10079,6 @@ "xpack.csp.findings.findingsFlyout.overviewTab.ruleNameTitle": "ルール名", "xpack.csp.findings.findingsFlyout.overviewTab.ruleTagsTitle": "ルールタグ", "xpack.csp.findings.findingsFlyout.overviewTabTitle": "概要", - "xpack.csp.findings.findingsFlyout.resourceTab.hostAccordionTitle": "ホスト", - "xpack.csp.findings.findingsFlyout.resourceTab.resourceAccordionTitle": "リソース", "xpack.csp.findings.findingsFlyout.resourceTabTitle": "リソース", "xpack.csp.findings.findingsFlyout.ruleTab.auditTitle": "監査", "xpack.csp.findings.findingsFlyout.ruleTab.benchmarkTitle": "ベンチマーク", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1ecd40512117e..6a0cb1c2ad138 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10094,8 +10094,6 @@ "xpack.csp.findings.findingsFlyout.overviewTab.ruleNameTitle": "规则名称", "xpack.csp.findings.findingsFlyout.overviewTab.ruleTagsTitle": "规则标签", "xpack.csp.findings.findingsFlyout.overviewTabTitle": "概览", - "xpack.csp.findings.findingsFlyout.resourceTab.hostAccordionTitle": "主机", - "xpack.csp.findings.findingsFlyout.resourceTab.resourceAccordionTitle": "资源", "xpack.csp.findings.findingsFlyout.resourceTabTitle": "资源", "xpack.csp.findings.findingsFlyout.ruleTab.auditTitle": "审计", "xpack.csp.findings.findingsFlyout.ruleTab.benchmarkTitle": "基准", From 8bf10c41af7fccde830a1d970242fe9d580d3417 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:41:44 +0200 Subject: [PATCH 65/68] [Cloud Security] Fix CSP pages remount (#151283) --- .../cloud_security_posture/public/plugin.tsx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/plugin.tsx b/x-pack/plugins/cloud_security_posture/public/plugin.tsx index 9114dcd9e2f4d..a941f1f770b13 100755 --- a/x-pack/plugins/cloud_security_posture/public/plugin.tsx +++ b/x-pack/plugins/cloud_security_posture/public/plugin.tsx @@ -67,19 +67,21 @@ export class CspPlugin Component: LazyCspCustomAssets, }); + // Keep as constant to prevent remounts https://github.com/elastic/kibana/issues/146773 + const App = (props: CspRouterProps) => ( + + +
+ + + +
+
+
+ ); + return { - getCloudSecurityPostureRouter: () => (props: CspRouterProps) => - ( - - -
- - - -
-
-
- ), + getCloudSecurityPostureRouter: () => App, }; } From e974e53b3206031b101de4a843b713bdb349bcdf Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:43:17 +0200 Subject: [PATCH 66/68] [Cloud Security] change `Belongs To` column to use runtime field (#151038) --- .../get_belongs_to_runtime_mapping.ts | 52 +++++++++++++++++++ .../get_identifier_runtime_mapping.ts | 0 .../findings_by_resource_table.test.tsx | 2 +- .../findings_by_resource_table.tsx | 21 +++++++- .../use_findings_by_resource.ts | 17 +++--- .../pages/findings/layout/findings_layout.tsx | 22 -------- .../collectors/accounts_stats_collector.ts | 2 +- .../collectors/resources_stats_collector.ts | 2 +- .../compliance_dashboard/get_clusters.ts | 2 +- .../server/tasks/findings_stats_task.ts | 2 +- 10 files changed, 86 insertions(+), 36 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.ts rename x-pack/plugins/cloud_security_posture/{server/lib => common/runtime_mappings}/get_identifier_runtime_mapping.ts (100%) diff --git a/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.ts b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.ts new file mode 100644 index 0000000000000..3ad655738e24e --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.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 { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; + +/** + * Creates the `belongs_to` runtime field with the value of either + * `account.cloud.name` or `cluster.id` based on the value of `rule.benchmark.posture_type` + */ +export const getBelongsToRuntimeMapping = (): MappingRuntimeFields => ({ + belongs_to: { + type: 'keyword', + script: { + source: ` + if (!doc.containsKey('rule.benchmark.posture_type')) + { + def identifier = doc["cluster_id"].value; + emit(identifier); + return + } + else + { + if(doc["rule.benchmark.posture_type"].size() > 0) + { + def policy_template_type = doc["rule.benchmark.posture_type"].value; + if (policy_template_type == "cspm") + { + def identifier = doc["cloud.account.name"].value; + emit(identifier); + return + } + + if (policy_template_type == "kspm") + { + def identifier = doc["cluster_id"].value; + emit(identifier); + return + } + } + + def identifier = doc["cluster_id"].value; + emit(identifier); + return + } + `, + }, + }, +}); diff --git a/x-pack/plugins/cloud_security_posture/server/lib/get_identifier_runtime_mapping.ts b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_identifier_runtime_mapping.ts similarity index 100% rename from x-pack/plugins/cloud_security_posture/server/lib/get_identifier_runtime_mapping.ts rename to x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_identifier_runtime_mapping.ts diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.test.tsx index a98cedafc6610..8de39503a9644 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.test.tsx @@ -26,7 +26,7 @@ const getFakeFindingsByResource = (): FindingsByResourcePage => { ); return { - cluster_id: chance.guid(), + belongs_to: chance.guid(), resource_id: chance.guid(), 'resource.name': resourceName, 'resource.sub_type': resourceSubtype, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx index 84c89d086a5f6..75f9e3a7fe14b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/findings_by_resource_table.tsx @@ -17,6 +17,8 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import numeral from '@elastic/numeral'; import { Link, generatePath } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { ColumnNameWithTooltip } from '../../../components/column_name_with_tooltip'; import { ComplianceScoreBar } from '../../../components/compliance_score_bar'; import * as TEST_SUBJECTS from '../test_subjects'; import type { FindingsByResourcePage } from './use_findings_by_resource'; @@ -79,7 +81,7 @@ const FindingsByResourceTableComponent = ({ getNonSortableColumn(findingsByResourceColumns['rule.benchmark.name']), { onAddFilter } ), - createColumnWithFilters(getNonSortableColumn(findingsByResourceColumns.cluster_id), { + createColumnWithFilters(getNonSortableColumn(findingsByResourceColumns.belongs_to), { onAddFilter, }), findingsByResourceColumns.compliance_score, @@ -153,7 +155,22 @@ const baseColumns: Array> = ); }, }, - baseFindingsColumns.cluster_id, + { + field: 'belongs_to', + name: ( + + ), + truncateText: true, + }, { field: 'compliance_score', width: '150px', diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts index 4a0110e0d536e..0442ff3424ad8 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts @@ -9,6 +9,7 @@ import { lastValueFrom } from 'rxjs'; import { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/common'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { Pagination } from '@elastic/eui'; +import { getBelongsToRuntimeMapping } from '../../../../common/runtime_mappings/get_belongs_to_runtime_mapping'; import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants'; import { useKibana } from '../../../common/hooks/use_kibana'; import { showErrorToast } from '../latest_findings/use_latest_findings'; @@ -43,7 +44,7 @@ export interface FindingsByResourcePage { }; compliance_score: number; resource_id: string; - cluster_id: string; + belongs_to: string; 'resource.name': string; 'resource.sub_type': string; 'rule.benchmark.name': string; @@ -62,7 +63,7 @@ interface FindingsAggBucket extends estypes.AggregationsStringRareTermsBucketKey passed_findings: estypes.AggregationsMultiBucketBase; name: estypes.AggregationsMultiBucketAggregateBase; subtype: estypes.AggregationsMultiBucketAggregateBase; - cluster_id: estypes.AggregationsMultiBucketAggregateBase; + belongs_to: estypes.AggregationsMultiBucketAggregateBase; benchmarkName: estypes.AggregationsMultiBucketAggregateBase; cis_sections: estypes.AggregationsMultiBucketAggregateBase; } @@ -75,6 +76,7 @@ export const getFindingsByResourceAggQuery = ({ body: { query, size: 0, + runtime_mappings: getBelongsToRuntimeMapping(), aggs: { ...getFindingsCountAggQuery(), resource_total: { cardinality: { field: 'resource.id' } }, @@ -99,8 +101,9 @@ export const getFindingsByResourceAggQuery = ({ passed_findings: { filter: { term: { 'result.evaluation': 'passed' } }, }, - cluster_id: { - terms: { field: 'cluster_id', size: 1 }, + // this field is runtime generated + belongs_to: { + terms: { field: 'belongs_to', size: 1 }, }, compliance_score: { bucket_script: { @@ -177,12 +180,12 @@ const createFindingsByResource = (resource: FindingsAggBucket): FindingsByResour !Array.isArray(resource.cis_sections.buckets) || !Array.isArray(resource.name.buckets) || !Array.isArray(resource.subtype.buckets) || - !Array.isArray(resource.cluster_id.buckets) || + !Array.isArray(resource.belongs_to.buckets) || !resource.benchmarkName.buckets.length || !resource.cis_sections.buckets.length || !resource.name.buckets.length || !resource.subtype.buckets.length || - !resource.cluster_id.buckets.length + !resource.belongs_to.buckets.length ) throw new Error('expected buckets to be an array'); @@ -190,9 +193,9 @@ const createFindingsByResource = (resource: FindingsAggBucket): FindingsByResour resource_id: resource.key, ['resource.name']: resource.name.buckets[0]?.key, ['resource.sub_type']: resource.subtype.buckets[0]?.key, - cluster_id: resource.cluster_id.buckets[0]?.key, ['rule.section']: resource.cis_sections.buckets.map((v) => v.key), ['rule.benchmark.name']: resource.benchmarkName.buckets[0]?.key, + belongs_to: resource.belongs_to.buckets[0]?.key, compliance_score: resource.compliance_score.value, findings: { failed_findings: resource.failed_findings.doc_count, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx index 1112d1f9d3688..4887c4f28e790 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx @@ -175,28 +175,6 @@ const baseColumns = [ ), }, - { - field: 'cluster_id', - name: ( - - ), - sortable: true, - truncateText: true, - render: (section: string) => ( - - <>{section} - - ), - }, { field: '@timestamp', align: 'right', diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts index 53e7aa66f5e3c..69cce5ed45893 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts @@ -10,7 +10,7 @@ import type { AggregationsMultiBucketBase, SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; -import { getIdentifierRuntimeMapping } from '../../get_identifier_runtime_mapping'; +import { getIdentifierRuntimeMapping } from '../../../../common/runtime_mappings/get_identifier_runtime_mapping'; import { calculatePostureScore } from '../../../../common/utils/helpers'; import type { CspmAccountsStats } from './types'; import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts index 7308ab7e91596..bccb92035a3bb 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts @@ -7,7 +7,7 @@ import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { Logger } from '@kbn/core/server'; import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; -import { getIdentifierRuntimeMapping } from '../../get_identifier_runtime_mapping'; +import { getIdentifierRuntimeMapping } from '../../../../common/runtime_mappings/get_identifier_runtime_mapping'; import type { CspmResourcesStats } from './types'; import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts index eb1f21efed659..736ba66a25508 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts @@ -22,7 +22,7 @@ import { import type { FailedFindingsQueryResult } from './get_grouped_findings_evaluation'; import { findingsEvaluationAggsQuery, getStatsFromFindingsEvaluationsAggs } from './get_stats'; import { KeyDocCount } from './compliance_dashboard'; -import { getIdentifierRuntimeMapping } from '../../lib/get_identifier_runtime_mapping'; +import { getIdentifierRuntimeMapping } from '../../../common/runtime_mappings/get_identifier_runtime_mapping'; export interface ClusterBucket extends FailedFindingsQueryResult, KeyDocCount { failed_findings: { diff --git a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts index 2895fb43d0b51..2e8401cebd932 100644 --- a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts +++ b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts @@ -14,7 +14,7 @@ import { import { SearchRequest } from '@kbn/data-plugin/common'; import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/core/server'; -import { getIdentifierRuntimeMapping } from '../lib/get_identifier_runtime_mapping'; +import { getIdentifierRuntimeMapping } from '../../common/runtime_mappings/get_identifier_runtime_mapping'; import { FindingsStatsTaskResult, TaskHealthStatus, ScoreByPolicyTemplateBucket } from './types'; import { BENCHMARK_SCORE_INDEX_DEFAULT_NS, From 860bc493cdf2634e39131c6755e874cce483b9cc Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Thu, 16 Feb 2023 11:46:52 +0100 Subject: [PATCH 67/68] Narrow scope of forced minimatch resolution (#151290) The "global" forced resolution of the `minimatch` package unintentionally downgraded some v5.x dependencies to v3.x. The only package where we need to force upgrade `minimatch` is the `globule` package which depends on `minimatch@~3.0.2`. So in this PR I've scoped the resolution to only concern itself with the `globule` dependencies. --- package.json | 2 +- yarn.lock | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4e48afef13c22..60d54b1c6f0ee 100644 --- a/package.json +++ b/package.json @@ -79,10 +79,10 @@ "**/chokidar": "^3.5.3", "**/deepmerge": "^4.2.2", "**/fast-deep-equal": "^3.1.1", + "**/globule/minimatch": "^3.1.2", "**/hoist-non-react-statics": "^3.3.2", "**/isomorphic-fetch/node-fetch": "^2.6.7", "**/istanbul-lib-coverage": "^3.2.0", - "**/minimatch": "^3.1.2", "**/pdfkit/crypto-js": "4.0.0", "**/refractor/prismjs": "~1.27.0", "**/trim": "1.0.1", diff --git a/yarn.lock b/yarn.lock index 06d1eb93ebdbf..98fd4c954e393 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11097,6 +11097,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + brace@0.11.1, brace@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" @@ -20901,13 +20908,27 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@5.0.1, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@^5.0.1, minimatch@~3.0.2: +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@~3.0.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" From 0581fa3a91ec840b83c739859041b37f4e3a92f9 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Thu, 16 Feb 2023 11:47:44 +0100 Subject: [PATCH 68/68] Bump refractor from v3.5.0 to v3.6.0 (#151296) The new version of `refractor` upgrades its dependency to `prismjs` to a version without security vulnerabilities. This means we can drop our forced resolution of `prismjs`. --- package.json | 1 - yarn.lock | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 60d54b1c6f0ee..09d980b233cd1 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "**/isomorphic-fetch/node-fetch": "^2.6.7", "**/istanbul-lib-coverage": "^3.2.0", "**/pdfkit/crypto-js": "4.0.0", - "**/refractor/prismjs": "~1.27.0", "**/trim": "1.0.1", "**/typescript": "4.6.3", "**/use-composed-ref": "^1.3.0", diff --git a/yarn.lock b/yarn.lock index 98fd4c954e393..3b8f7ec40dad0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23296,7 +23296,7 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prismjs@^1.22.0, prismjs@~1.25.0, prismjs@~1.27.0: +prismjs@^1.22.0, prismjs@~1.27.0: version "1.27.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== @@ -24609,13 +24609,13 @@ redux@^4.0.0, redux@^4.0.4, redux@^4.1.2, redux@^4.2.0: "@babel/runtime" "^7.9.2" refractor@^3.2.0, refractor@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.5.0.tgz#334586f352dda4beaf354099b48c2d18e0819aec" - integrity sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg== + version "3.6.0" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" + integrity sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA== dependencies: hastscript "^6.0.0" parse-entities "^2.0.0" - prismjs "~1.25.0" + prismjs "~1.27.0" regedit@^5.0.0: version "5.0.0"